#include "picasso.h" //#define DEBUG #define BUF g_outputBuf #define NO_MORE_STACK (g_stackPos==MAX_STACK) static const char* curFile = NULL; static int curLine = -1; std::vector g_outputBuf; StackEntry g_stack[MAX_STACK]; int g_stackPos; int g_opdescTable[MAX_OPDESC]; int g_opdescCount; int g_opdescMasks[MAX_OPDESC]; Uniform g_uniformTable[MAX_UNIFORM]; int g_uniformCount; class UniformAlloc { int start, end, bound, tend; public: UniformAlloc(int start, int end) : start(start), end(end), bound(end), tend(end) { } void ClearLocal(void) { end = tend; } int AllocGlobal(int size) { if ((start+size) > bound) return -1; int ret = start; start += size; return ret; } int AllocLocal(int size) { int pos = end - size; if (pos < start) return -1; bound = pos < bound ? pos : bound; return pos; } }; static UniformAlloc fvecAlloc(0x20, 0x80), ivecAlloc(0x80, 0x84), boolAlloc(0x88, 0x98); procTableType g_procTable; dvleTableType g_dvleTable; relocTableType g_procRelocTable; int g_totalDvleCount; labelTableType g_labels; relocTableType g_labelRelocTable; aliasTableType g_aliases; static DVLEData* curDvle; static void ClearStatus(void) { fvecAlloc.ClearLocal(); ivecAlloc.ClearLocal(); boolAlloc.ClearLocal(); g_labels.clear(); g_labelRelocTable.clear(); g_aliases.clear(); curDvle = NULL; } static DVLEData* GetDvleData(void) { if (!curDvle) { g_dvleTable.push_back( DVLEData(curFile) ); curDvle = &g_dvleTable.back(); g_totalDvleCount ++; } return curDvle; } static char* mystrtok_pos; static char* mystrtok(char* str, const char* delim) { if (!str) str = mystrtok_pos; if (!*str) return NULL; size_t pos = strcspn(str, delim); char* ret = str; str += pos; if (*str) *str++ = 0; mystrtok_pos = str; return ret; } static char* mystrtok_spc(char* str) { char* ret = mystrtok(str, " \t"); if (!ret) return NULL; if (*mystrtok_pos) for (; *mystrtok_pos && isspace(*mystrtok_pos); mystrtok_pos++); return ret; } static char* remove_comment(char* buf) { char* pos = strchr(buf, ';'); if (pos) *pos = 0; return buf; } static char* trim_whitespace(char* buf) { if (!buf) return NULL; // Remove trailing whitespace int pos; for(pos = strlen(buf)-1; pos >= 0 && isspace(buf[pos]); pos --) buf[pos] = '\0'; // Remove leading whitespace char* newbuf = buf; for(; isspace(*newbuf); newbuf ++); return newbuf; } static bool validateIdentifier(const char* id) { int len = strlen(id); bool valid = true; for (int i = 0; valid && i < len; i ++) { int c = id[i]; valid = isalpha(c) || c == '_' || c == '.' || (i > 0 && isdigit(c)); } return valid; } static int throwError(const char* msg, ...) { va_list v; fprintf(stderr, "%s:%d: error: ", curFile, curLine); va_start(v, msg); vfprintf(stderr, msg, v); va_end(v); return 1; } static int parseInt(char* pos, int& out, long long min, long long max) { char* endptr = NULL; long long res = strtoll(pos, &endptr, 0); if (pos == endptr) return throwError("Invalid value: %s\n", pos); if (res < min || res > max) return throwError("Value out of range (%d..%u): %d\n", (int)min, (unsigned int)max, (int)res); out = res; return 0; } #define safe_call(x) do \ { \ int _ = (x); \ if (_ != 0) return _; \ } while(0) static int ProcessCommand(const char* cmd); static int FixupLabelRelocations(); int AssembleString(char* str, const char* initialFilename) { curFile = initialFilename; curLine = 1; ClearStatus(); int nextLineIncr = 0; char* nextStr = NULL; for (; str; str = nextStr, curLine += nextLineIncr) { size_t len = strcspn(str, "\n"); int linedelim = str[len]; str[len] = 0; nextStr = linedelim ? (str + len + 1) : NULL; nextLineIncr = linedelim == '\n' ? 1 : 0; char* line = trim_whitespace(remove_comment(str)); char* colonPos = NULL; for (;;) { colonPos = strchr(line, ':'); if (!colonPos) break; *colonPos = 0; char* labelName = line; line = trim_whitespace(colonPos + 1); if (!validateIdentifier(labelName)) return throwError("invalid label name: %s\n", labelName); std::pair ret = g_labels.insert( std::pair(labelName, BUF.size()) ); if (!ret.second) return throwError("duplicate label: %s\n", labelName); //printf("Label: %s\n", labelName); }; if (!*line) continue; if (*line == '#') { line = trim_whitespace(line + 1); nextLineIncr = 0; size_t pos = strcspn(line, " \t"); line[pos] = 0; curLine = atoi(line); line = trim_whitespace(line + pos + 1); if (*line == '"') { line ++; line[strlen(line)-1] = 0; } curFile = line; continue; } char* tok = mystrtok_spc(line); safe_call(ProcessCommand(tok)); } if (g_stackPos) return throwError("unclosed block(s)\n"); safe_call(FixupLabelRelocations()); return 0; } int FixupLabelRelocations() { for (relocTableIter it = g_labelRelocTable.begin(); it != g_labelRelocTable.end(); ++it) { relocation& r = *it; u32& inst = BUF[r.first]; labelTableIter lbl = g_labels.find(r.second); if (lbl == g_labels.end()) return throwError("label '%s' is undefined\n", r.second.c_str()); u32 dst = lbl->second; inst &= ~(0xFFF << 10); inst |= dst << 10; } return 0; } int RelocateProduct() { for (relocTableIter it = g_procRelocTable.begin(); it != g_procRelocTable.end(); ++it) { relocation& r = *it; u32& inst = BUF[r.first]; procTableIter proc = g_procTable.find(r.second); if (proc == g_procTable.end()) return throwError("procedure '%s' is undefined\n", r.second.c_str()); u32 dst = proc->second.first; u32 num = proc->second.second; inst &= ~0x3FFFFF; inst |= num | (dst << 10); } if (g_totalDvleCount == 0) return throwError("no DVLEs can be generated from the given input file(s)\n"); for (dvleTableIter it = g_dvleTable.begin(); it != g_dvleTable.end(); ++it) { if (it->nodvle) continue; curFile = it->filename.c_str(); curLine = 1; procTableIter mainIt = g_procTable.find(it->entrypoint); if (mainIt == g_procTable.end()) return throwError("entrypoint '%s' is undefined\n", it->entrypoint.c_str()); it->entryStart = mainIt->second.first; it->entryEnd = it->entryStart + mainIt->second.second; } return 0; } // -------------------------------------------------------------------- // Commands // -------------------------------------------------------------------- static char* nextArg() { return trim_whitespace(mystrtok(NULL, ",")); } static char* nextArgCParen() { return trim_whitespace(mystrtok(NULL, "(")); } static char* nextArgSpc() { return trim_whitespace(mystrtok_spc(NULL)); } static int missingParam() { return throwError("missing parameter\n"); } typedef struct { const char* name; int (* func) (const char*, int, int); int opcode, opcodei; } cmdTableType; #define NEXT_ARG(_varName) char* _varName; do \ { \ _varName = nextArg(); \ if (!_varName) return missingParam(); \ } while (0) #define NEXT_ARG_SPC(_varName) char* _varName; do \ { \ _varName = nextArgSpc(); \ if (!_varName) return missingParam(); \ } while (0) #define NEXT_ARG_CPAREN(_varName) char* _varName; do \ { \ _varName = nextArgCParen(); \ if (!_varName) return missingParam(); \ } while (0) #define NEXT_ARG_OPT(_varName, _opt) char* _varName; do \ { \ _varName = nextArg(); \ if (!_varName) _varName = (char*)(_opt); \ } while (0) #define DEF_COMMAND(name) \ static int cmd_##name(const char* cmdName, int opcode, int opcodei) #define DEC_COMMAND(name, fun) \ { #name, cmd_##fun, MAESTRO_##name, -1 } #define DEC_COMMAND2(name, fun) \ { #name, cmd_##fun, MAESTRO_##name, MAESTRO_##name##I }, \ { #name "i", cmd_##fun, MAESTRO_##name, MAESTRO_##name##I } #define DEF_DIRECTIVE(name) \ static int dir_##name(const char* cmdName, int dirParam, int _unused) #define DEC_DIRECTIVE(name) \ { #name, dir_##name, 0, 0 } #define DEC_DIRECTIVE2(name, fun, opc) \ { #name, dir_##fun, opc, 0 } static int ensureNoMoreArgs() { return nextArg() ? throwError("too many parameters\n") : 0; } static int duplicateIdentifier(const char* id) { return throwError("identifier already used: %s\n", id); } static int ensureTarget(const char* target) { if (!validateIdentifier(target)) return throwError("invalid target: %s\n", target); return 0; } static inline int ensure_valid_dest(int reg, const char* name) { if (reg < 0x00 || reg >= 0x20) return throwError("invalid destination register: %s\n", name); return 0; } static inline int ensure_valid_src_wide(int reg, const char* name, int srcId) { if (reg < 0x00 || reg >= 0x80) return throwError("invalid source%d register: %s\n", srcId, name); return 0; } static inline int ensure_valid_src_narrow(int reg, const char* name, int srcId) { if (reg < 0x00 || reg >= 0x20) return throwError("invalid source%d register: %s\n", srcId, name); return 0; } static inline int ensure_no_idxreg(int idxreg, int srcId) { if (idxreg > 0) return throwError("index register not allowed in source%d\n", srcId); return 0; } static inline int ensure_valid_ireg(int reg, const char* name) { if (reg < 0x80 || reg >= 0x88) return throwError("invalid integer vector uniform: %s\n", name); return 0; } static inline int ensure_valid_breg(int reg, const char* name) { if (reg < 0x88 || reg >= 0x98) return throwError("invalid boolean uniform: %s\n", name); return 0; } static inline int ensure_valid_condop(int condop, const char* name) { if (condop < 0) return throwError("invalid conditional operator: %s\n", name); return 0; } #define ENSURE_NO_MORE_ARGS() safe_call(ensureNoMoreArgs()) #define ARG_TO_INT(_varName, _argName, _min, _max) \ int _varName = 0; \ safe_call(parseInt(_argName, _varName, _min, _max)) #define ARG_TO_REG(_varName, _argName) \ int _varName = 0, _varName##Sw = 0; \ safe_call(parseReg(_argName, _varName, _varName##Sw)); #define ARG_TO_REG2(_varName, _argName) \ int _varName = 0, _varName##Sw = 0, _varName##Idx = 0; \ safe_call(parseReg(_argName, _varName, _varName##Sw, &_varName##Idx)); #define ARG_TO_CONDOP(_varName, _argName) \ int _varName = parseCondOp(_argName); \ safe_call(ensure_valid_condop(_varName, _argName)) #define ARG_TARGET(_argName) \ safe_call(ensureTarget(_argName)) #define ARG_TO_DEST_REG(_reg, _name) \ ARG_TO_REG(_reg, _name); \ safe_call(ensure_valid_dest(_reg, _name)) #define ARG_TO_SRC1_REG(_reg, _name) \ ARG_TO_REG(_reg, _name); \ safe_call(ensure_valid_src_wide(_reg, _name, 1)) #define ARG_TO_SRC1_REG2(_reg, _name) \ ARG_TO_REG2(_reg, _name); \ safe_call(ensure_valid_src_wide(_reg, _name, 1)) #define ARG_TO_SRC2_REG(_reg, _name) \ ARG_TO_REG(_reg, _name); \ safe_call(ensure_valid_src_narrow(_reg, _name, 2)) #define ARG_TO_IREG(_reg, _name) \ ARG_TO_REG(_reg, _name); \ safe_call(ensure_valid_ireg(_reg, _name)) #define ARG_TO_BREG(_reg, _name) \ ARG_TO_REG(_reg, _name); \ safe_call(ensure_valid_breg(_reg, _name)) static int parseSwizzling(const char* b) { int i, out = 0, q = COMP_X; for (i = 0; b[i] && i < 4; i ++) { switch (tolower(b[i])) { case 'x': q = COMP_X; break; case 'y': q = COMP_Y; break; case 'z': q = COMP_Z; break; case 'w': q = COMP_W; break; default: return -1; } out |= SWIZZLE_COMP(i, q); } if (b[i]) return -1; // Fill in missing bits for (int j = i; j < 4; j ++) out |= SWIZZLE_COMP(j, q); return out<<1; } static int maskFromSwizzling(int sw, bool reverse = true) { sw >>= 1; // get rid of negation bit int out = 0; for (int i = 0; i < 4; i ++) { int bitid = (sw>>(i*2))&3; if (reverse) bitid = 3 - bitid; out |= BIT(bitid); } return out; } static int findOrAddOpdesc(int& out, int opdesc, int mask) { for (int i = 0; i < g_opdescCount; i ++) { int minMask = mask & g_opdescMasks[i]; if ((opdesc&minMask) == (g_opdescTable[i]&minMask)) { // Save extra bits, if any g_opdescTable[i] |= opdesc & (mask^minMask); g_opdescMasks[i] |= mask; out = i; return 0; } } if (g_opdescCount == MAX_OPDESC) return throwError("too many operand descriptors (limit is %d)\n", MAX_OPDESC); g_opdescTable[g_opdescCount] = opdesc; g_opdescMasks[g_opdescCount] = mask; out = g_opdescCount++; return 0; } static inline bool isregp(int x) { x = tolower(x); return x=='o' || x=='v' || x=='r' || x=='c' || x=='i' || x=='b'; } static inline int convertIdxRegName(const char* reg) { if (stricmp(reg, "a0")==0) return 1; if (stricmp(reg, "a1")==0) return 2; if (stricmp(reg, "a2")==0 || stricmp(reg, "lcnt")==0) return 2; return 0; } static inline int parseCondOp(const char* name) { if (stricmp(name, "eq")==0) return COND_EQ; if (stricmp(name, "ne")==0) return COND_NE; if (stricmp(name, "lt")==0) return COND_LT; if (stricmp(name, "le")==0) return COND_LE; if (stricmp(name, "gt")==0) return COND_GT; if (stricmp(name, "ge")==0) return COND_GE; if (*name == '6') return COND_UNK1; if (*name == '7') return COND_UNK2; return -1; } static int parseReg(char* pos, int& outReg, int& outSw, int* idxType = NULL) { outReg = 0; outSw = DEFAULT_OPSRC; if (idxType) *idxType = 0; if (*pos == '-') { pos++; outSw |= 1; // negation bit } char* dotPos = strchr(pos, '.'); if (dotPos) { *dotPos++ = 0; outSw = parseSwizzling(dotPos) | (outSw&1); if (outSw < 0) return throwError("invalid swizzling mask: %s\n", dotPos); } int regOffset = 0; char* offPos = strchr(pos, '['); if (offPos) { char* closePos = strchr(offPos, ']'); if (!closePos) return throwError("missing closing bracket: %s\n", pos); *closePos = 0; *offPos++ = 0; offPos = trim_whitespace(offPos); // Check for idxreg+offset int temp = convertIdxRegName(offPos); if (temp>0) { if (!idxType) return throwError("index register not allowed here: %s\n", offPos); *idxType = temp; } else do { char* plusPos = strchr(offPos, '+'); if (!plusPos) break; if (!idxType) return throwError("index register not allowed here: %s\n", offPos); *plusPos++ = 0; char* idxRegName = trim_whitespace(offPos); offPos = trim_whitespace(plusPos); *idxType = convertIdxRegName(idxRegName); if (*idxType < 0) return throwError("invalid index register: %s\n", idxRegName); } while (0); regOffset = atoi(offPos); if (regOffset < 0) return throwError("invalid register offset: %s\n", offPos); } aliasTableIter it = g_aliases.find(pos); if (it != g_aliases.end()) { int x = it->second; outReg = x & 0xFF; outReg += regOffset; outSw ^= (x>>8)&1; x >>= 9; // Combine swizzling int temp = outSw & 1; for (int j = 0; j < 4; j ++) { int comp = (outSw >> (7 - j*2)) & 3; comp = (x >> (6 - comp*2)) & 3; temp |= SWIZZLE_COMP(j, comp)<<1; } outSw = temp; return 0; } if (!isregp(pos[0]) || !isdigit(pos[1])) return throwError("invalid register: %s\n", pos); safe_call(parseInt(pos+1, outReg, 0, 255)); switch (*pos) { case 'o': // Output registers if (outReg < 0x00 || outReg >= 0x08) return throwError("invalid output register: %s(%d)\n", pos); break; case 'v': // Input attributes if (outReg < 0x00 || outReg >= 0x0F) return throwError("invalid input register: %s(%d)\n", pos); break; case 'r': // Temporary registers outReg += 0x10; if (outReg < 0x10 || outReg >= 0x20) return throwError("invalid temporary register: %s(%d)\n", pos); break; case 'c': // Floating-point vector uniform registers outReg += 0x20; if (outReg < 0x20 || outReg >= 0x80) return throwError("invalid floating-point vector uniform register: %s(%d)\n", pos); break; case 'i': // Integer vector uniforms outReg += 0x80; if (outReg < 0x80 || outReg >= 0x88) return throwError("invalid integer vector uniform register: %s(%d)\n", pos); break; case 'b': // Boolean uniforms outReg += 0x88; if (outReg < 0x88 || outReg >= 0x98) return throwError("invalid boolean uniform register: %s(%d)\n", pos); break; } outReg += regOffset; return 0; } static int parseCondExpOp(char* str, u32& outFlags, int& which) { int negation = 0; for (; *str == '!'; str++) negation ^= 1; if (stricmp(str, "cmp.x")==0) { which = 0; outFlags ^= negation<<25; return 0; } if (stricmp(str, "cmp.y")==0) { which = 1; outFlags ^= negation<<24; return 0; } return throwError("invalid condition register: %s\n", str); } static int parseCondExp(char* str, u32& outFlags) { outFlags = BIT(24) | BIT(25); size_t len = strlen(str); size_t pos = strcspn(str, "&|"); int op2 = -1; if (pos < len) { char* str2 = str + pos; int type = *str2; *str2++ = 0; if (*str2 == type) str2++; str = trim_whitespace(str); str2 = trim_whitespace(str2); if (type == '&') outFlags |= 1<<22; safe_call(parseCondExpOp(str2, outFlags, op2)); } int op1 = -1; safe_call(parseCondExpOp(str, outFlags, op1)); if (op1 == op2) return throwError("condition register checked twice\n"); if (op2 < 0) outFlags |= (op1+2)<<22; return 0; } DEF_COMMAND(format0) { ENSURE_NO_MORE_ARGS(); BUF.push_back(FMT_OPCODE(opcode)); return 0; } DEF_COMMAND(format1) { NEXT_ARG(destName); NEXT_ARG(src1Name); NEXT_ARG(src2Name); ENSURE_NO_MORE_ARGS(); ARG_TO_DEST_REG(rDest, destName); ARG_TO_REG2(rSrc1, src1Name); ARG_TO_REG2(rSrc2, src2Name); bool inverted = opcodei >= 0 && rSrc1 < 0x20 && rSrc2 >= 0x20; if (!inverted) { safe_call(ensure_valid_src_wide(rSrc1, src1Name, 1)); safe_call(ensure_valid_src_narrow(rSrc2, src2Name, 2)); safe_call(ensure_no_idxreg(rSrc2Idx, 2)); } else { safe_call(ensure_valid_src_narrow(rSrc1, src1Name, 1)); safe_call(ensure_no_idxreg(rSrc1Idx, 1)); safe_call(ensure_valid_src_wide(rSrc2, src2Name, 2)); } int opdesc = 0; safe_call(findOrAddOpdesc(opdesc, OPDESC_MAKE(maskFromSwizzling(rDestSw), rSrc1Sw, rSrc2Sw, 0), OPDESC_MASK_D12)); #ifdef DEBUG printf("%s:%02X d%02X, d%02X, d%02X (0x%X)\n", cmdName, opcode, rDest, rSrc1, rSrc2, opdesc); #endif if (!inverted) BUF.push_back(FMT_OPCODE(opcode) | opdesc | (rSrc2<<7) | (rSrc1<<12) | (rSrc1Idx<<19) | (rDest<<21)); else BUF.push_back(FMT_OPCODE(opcodei) | opdesc | (rSrc2<<7) | (rSrc1<<14) | (rSrc2Idx<<19) | (rDest<<21)); return 0; } DEF_COMMAND(format1u) { NEXT_ARG(destName); NEXT_ARG(src1Name); ENSURE_NO_MORE_ARGS(); ARG_TO_DEST_REG(rDest, destName); ARG_TO_SRC1_REG2(rSrc1, src1Name); int opdesc = 0; safe_call(findOrAddOpdesc(opdesc, OPDESC_MAKE(maskFromSwizzling(rDestSw), rSrc1Sw, 0, 0), OPDESC_MASK_D1)); #ifdef DEBUG printf("%s:%02X d%02X, d%02X (0x%X)\n", cmdName, opcode, rDest, rSrc1, opdesc); #endif BUF.push_back(FMT_OPCODE(opcode) | opdesc | (rSrc1<<12) | (rSrc1Idx<<19) | (rDest<<21)); return 0; } DEF_COMMAND(format1c) { NEXT_ARG(src1Name); NEXT_ARG(cmpxName); NEXT_ARG(cmpyName); NEXT_ARG(src2Name); ENSURE_NO_MORE_ARGS(); ARG_TO_SRC1_REG2(rSrc1, src1Name); ARG_TO_CONDOP(cmpx, cmpxName); ARG_TO_CONDOP(cmpy, cmpyName); ARG_TO_SRC2_REG(rSrc2, src2Name); int opdesc = 0; safe_call(findOrAddOpdesc(opdesc, OPDESC_MAKE(0, rSrc1Sw, rSrc2Sw, 0), OPDESC_MASK_12)); #ifdef DEBUG printf("%s:%02X d%02X, %d, %d, d%02X (0x%X)\n", cmdName, opcode, rSrc1, cmpx, cmpy, rSrc2, opdesc); #endif BUF.push_back(FMT_OPCODE(opcode) | opdesc | (rSrc2<<7) | (rSrc1<<12) | (rSrc1Idx<<19) | (cmpy<<21) | (cmpx<<24)); return 0; } DEF_COMMAND(format5) { NEXT_ARG(destName); NEXT_ARG(src1Name); NEXT_ARG(src2Name); NEXT_ARG(src3Name); ENSURE_NO_MORE_ARGS(); ARG_TO_DEST_REG(rDest, destName); ARG_TO_SRC1_REG(rSrc1, src1Name); ARG_TO_REG(rSrc2, src2Name); ARG_TO_REG(rSrc3, src3Name); bool inverted = opcodei >= 0 && rSrc2 < 0x20 && rSrc3 >= 0x20; if (!inverted) { safe_call(ensure_valid_src_wide(rSrc2, src2Name, 2)); safe_call(ensure_valid_src_narrow(rSrc3, src3Name, 3)); } else { safe_call(ensure_valid_src_narrow(rSrc2, src2Name, 2)); safe_call(ensure_valid_src_wide(rSrc3, src3Name, 3)); } int opdesc = 0; safe_call(findOrAddOpdesc(opdesc, OPDESC_MAKE(maskFromSwizzling(rDestSw), rSrc1Sw, rSrc2Sw, rSrc3Sw), OPDESC_MASK_D123)); if (opdesc >= 32) return throwError("opdesc allocation error\n"); #ifdef DEBUG printf("%s:%02X d%02X, d%02X, d%02X, d%02X (0x%X)\n", cmdName, opcode, rDest, rSrc1, rSrc2, rSrc3, opdesc); #endif if (!inverted) BUF.push_back(FMT_OPCODE(opcode) | opdesc | (rSrc3<<5) | (rSrc2<<10) | (rSrc1<<17) | (rDest<<24)); else BUF.push_back(FMT_OPCODE(opcodei) | opdesc | (rSrc3<<5) | (rSrc2<<12) | (rSrc1<<17) | (rDest<<24)); return 0; } DEF_COMMAND(formatmova) { NEXT_ARG(src1Name); ENSURE_NO_MORE_ARGS(); ARG_TO_SRC1_REG2(rSrc1, src1Name); int opdesc = 0; safe_call(findOrAddOpdesc(opdesc, OPDESC_MAKE(0, rSrc1Sw, 0, 0), OPDESC_MASK_1)); #ifdef DEBUG printf("%s:%02X d%02X (0x%X)\n", cmdName, opcode, rSrc1, opdesc); #endif BUF.push_back(FMT_OPCODE(opcode) | opdesc | (rSrc1<<12) | (rSrc1Idx<<19)); return 0; } static inline int parseSetEmitFlags(char* flags, bool& isPrim, bool& isInv) { isPrim = false; isInv = false; if (!flags) return 0; mystrtok_pos = flags; while (char* flag = mystrtok_spc(NULL)) { if (stricmp(flag, "prim")==0 || stricmp(flag, "primitive")==0) isPrim = true; else if (stricmp(flag, "inv")==0 || stricmp(flag, "invert")==0) isInv = true; else throwError("unknown setemit flag: %s\n", flag); } return 0; } DEF_COMMAND(formatsetemit) { NEXT_ARG(vtxIdStr); NEXT_ARG_OPT(flagStr, NULL); ENSURE_NO_MORE_ARGS(); ARG_TO_INT(vtxId, vtxIdStr, 0, 3); bool isPrim, isInv; safe_call(parseSetEmitFlags(flagStr, isPrim, isInv)); #ifdef DEBUG printf("%s:%02X vtx%d, %s, %s\n", cmdName, opcode, vtxId, isPrim?"true":"false", isInv?"true":"false"); #endif BUF.push_back(FMT_OPCODE(opcode) | ((u32)isInv<<22) | ((u32)isPrim<<23) | (vtxId<<24)); GetDvleData()->isGeoShader = true; return 0; } DEF_COMMAND(formatcall) { NEXT_ARG(procName); ENSURE_NO_MORE_ARGS(); ARG_TARGET(procName); g_procRelocTable.push_back( std::make_pair(BUF.size(), procName) ); BUF.push_back(FMT_OPCODE(opcode)); #ifdef DEBUG printf("%s:%02X %s\n", cmdName, opcode, procName); #endif return 0; } DEF_COMMAND(formatfor) { NEXT_ARG(regName); ENSURE_NO_MORE_ARGS(); ARG_TO_IREG(regId, regName); if (NO_MORE_STACK) return throwError("too many nested blocks\n"); StackEntry& elem = g_stack[g_stackPos++]; elem.type = SE_FOR; elem.pos = BUF.size(); BUF.push_back(FMT_OPCODE(opcode) | ((regId-0x80) << 22)); #ifdef DEBUG printf("%s:%02X d%02X\n", cmdName, opcode, regId); #endif return 0; } DEF_COMMAND(format2) { NEXT_ARG(condExp); u32 instruction = 0; safe_call(parseCondExp(condExp, instruction)); switch (opcode) { case MAESTRO_BREAKC: { ENSURE_NO_MORE_ARGS(); #ifdef DEBUG printf("%s:%02X %s\n", cmdName, opcode, condExp); #endif break; } case MAESTRO_CALLC: case MAESTRO_JMPC: { NEXT_ARG(targetName); ENSURE_NO_MORE_ARGS(); ARG_TARGET(targetName); relocTableType& rt = opcode==MAESTRO_CALLC ? g_procRelocTable : g_labelRelocTable; rt.push_back( std::make_pair(BUF.size(), targetName) ); #ifdef DEBUG printf("%s:%02X %s, %s\n", cmdName, opcode, condExp, targetName); #endif break; } case MAESTRO_IFC: { ENSURE_NO_MORE_ARGS(); if (NO_MORE_STACK) return throwError("too many nested blocks\n"); StackEntry& elem = g_stack[g_stackPos++]; elem.type = SE_IF; elem.pos = BUF.size(); elem.uExtra = 0; #ifdef DEBUG printf("%s:%02X %s\n", cmdName, opcode, condExp); #endif break; } } BUF.push_back(FMT_OPCODE(opcode) | instruction); return 0; } DEF_COMMAND(format3) { NEXT_ARG(regName); ARG_TO_BREG(regId, regName); switch (opcode) { case MAESTRO_CALLU: case MAESTRO_JMPU: { NEXT_ARG(targetName); ENSURE_NO_MORE_ARGS(); ARG_TARGET(targetName); relocTableType& rt = opcode==MAESTRO_CALLU ? g_procRelocTable : g_labelRelocTable; rt.push_back( std::make_pair(BUF.size(), targetName) ); #ifdef DEBUG printf("%s:%02X d%02X, %s\n", cmdName, opcode, regId, targetName); #endif break; } case MAESTRO_IFU: { ENSURE_NO_MORE_ARGS(); if (NO_MORE_STACK) return throwError("too many nested blocks\n"); StackEntry& elem = g_stack[g_stackPos++]; elem.type = SE_IF; elem.pos = BUF.size(); elem.uExtra = 0; #ifdef DEBUG printf("%s:%02X d%02X\n", cmdName, opcode, regId); #endif break; } } BUF.push_back(FMT_OPCODE(opcode) | ((regId-0x88) << 22)); return 0; } static const cmdTableType cmdTable[] = { DEC_COMMAND(NOP, format0), DEC_COMMAND(END, format0), DEC_COMMAND(EMIT, format0), DEC_COMMAND(ADD, format1), DEC_COMMAND(DP3, format1), DEC_COMMAND(DP4, format1), DEC_COMMAND2(DPH, format1), DEC_COMMAND(MUL, format1), DEC_COMMAND2(SGE, format1), DEC_COMMAND2(SLT, format1), DEC_COMMAND(MAX, format1), DEC_COMMAND(MIN, format1), DEC_COMMAND(EX2, format1u), DEC_COMMAND(LG2, format1u), DEC_COMMAND(FLR, format1u), DEC_COMMAND(RCP, format1u), DEC_COMMAND(RSQ, format1u), DEC_COMMAND(MOV, format1u), DEC_COMMAND(MOVA, formatmova), DEC_COMMAND(CMP, format1c), DEC_COMMAND(CALL, formatcall), DEC_COMMAND(FOR, formatfor), DEC_COMMAND(BREAKC, format2), DEC_COMMAND(CALLC, format2), DEC_COMMAND(IFC, format2), DEC_COMMAND(JMPC, format2), DEC_COMMAND(CALLU, format3), DEC_COMMAND(IFU, format3), DEC_COMMAND(JMPU, format3), DEC_COMMAND2(MAD, format5), DEC_COMMAND(SETEMIT, formatsetemit), { NULL, NULL }, }; // -------------------------------------------------------------------- // Directives // -------------------------------------------------------------------- DEF_DIRECTIVE(proc) { NEXT_ARG(procName); ENSURE_NO_MORE_ARGS(); if (NO_MORE_STACK) return throwError("too many nested blocks\n"); StackEntry& elem = g_stack[g_stackPos++]; elem.type = SE_PROC; elem.pos = BUF.size(); elem.strExtra = procName; if (g_procTable.find(procName) != g_procTable.end()) return throwError("proc already exists: %s\n", procName); #ifdef DEBUG printf("Defining %s\n", procName); #endif return 0; } DEF_DIRECTIVE(else) { ENSURE_NO_MORE_ARGS(); if (!g_stackPos) return throwError(".else with unmatched IF\n"); StackEntry& elem = g_stack[g_stackPos-1]; if (elem.type != SE_IF) return throwError(".else with unmatched IF\n"); if (elem.uExtra) return throwError("spurious .else\n"); u32 curPos = BUF.size(); elem.uExtra = curPos; u32& inst = BUF[elem.pos]; inst &= ~(0xFFF << 10); inst |= curPos << 10; #ifdef DEBUG printf("ELSE\n"); #endif return 0; } DEF_DIRECTIVE(end) { ENSURE_NO_MORE_ARGS(); if (!g_stackPos) return throwError(".end with unmatched block\n"); StackEntry& elem = g_stack[--g_stackPos]; u32 curPos = BUF.size(); u32 size = curPos - elem.pos; switch (elem.type) { case SE_PROC: { #ifdef DEBUG printf("proc: %s(%u, size:%u)\n", elem.strExtra, elem.pos, size); #endif g_procTable.insert( std::pair(elem.strExtra, procedure(elem.pos, size)) ); break; } case SE_FOR: { #ifdef DEBUG printf("ENDFOR\n"); #endif u32& inst = BUF[elem.pos]; inst &= ~(0xFFF << 10); inst |= (curPos-1) << 10; break; } case SE_IF: { #ifdef DEBUG printf("ENDIF\n"); #endif u32& inst = BUF[elem.pos]; if (!elem.uExtra) { // IF with no ELSE inst &= ~(0xFFF << 10); inst |= curPos << 10; } else { // IF with an ELSE inst &= ~0x3FF; inst |= curPos - elem.uExtra; } break; } } return 0; } DEF_DIRECTIVE(alias) { NEXT_ARG_SPC(aliasName); NEXT_ARG_SPC(aliasReg); ENSURE_NO_MORE_ARGS(); if (!validateIdentifier(aliasName)) return throwError("invalid alias name: %s\n", aliasName); if (isregp(aliasName[0]) && isdigit(aliasName[1])) return throwError("cannot redefine register\n"); ARG_TO_REG(rAlias, aliasReg); if (g_aliases.find(aliasName) != g_aliases.end()) return duplicateIdentifier(aliasName); g_aliases.insert( std::pair(aliasName, rAlias | (rAliasSw<<8)) ); return 0; } static inline UniformAlloc& getAlloc(int type) { switch (type) { default: case UTYPE_FVEC: return fvecAlloc; case UTYPE_IVEC: return ivecAlloc; case UTYPE_BOOL: return boolAlloc; } } DEF_DIRECTIVE(uniform) { UniformAlloc& alloc = getAlloc(dirParam); for (;;) { char* argText = nextArg(); if (!argText) break; int uSize = 1; char* sizePos = strchr(argText, '['); if (sizePos) { char* closePos = strchr(sizePos, ']'); if (!closePos) return throwError("missing closing bracket: %s\n", argText); *closePos = 0; *sizePos++ = 0; sizePos = trim_whitespace(sizePos); uSize = atoi(sizePos); if (uSize < 1) return throwError("invalid uniform size: %s[%s]\n", argText, sizePos); } if (!validateIdentifier(argText)) return throwError("invalid uniform name: %s\n", argText); if (g_aliases.find(argText) != g_aliases.end()) return duplicateIdentifier(argText); int uniformPos = -1; // Find the uniform in the table int i; for (i = 0; i < g_uniformCount; i ++) { Uniform& uniform = g_uniformTable[i]; if (uniform.name == argText) { if (uniform.type != dirParam) return throwError("mismatched uniform type: %s\n", argText); if (uniform.size != uSize) return throwError("uniform '%s' previously declared as having size %d\n", argText, uniform.size); break; } } // If not found, create it if (i == g_uniformCount && uniformPos < 0) { if (g_uniformCount == MAX_UNIFORM) return throwError("too many global uniforms: %s\n", argText); uniformPos = alloc.AllocGlobal(uSize); if (uniformPos < 0) return throwError("not enough uniform space: %s[%d]\n", argText, uSize); } Uniform& uniform = g_uniformTable[g_uniformCount++]; uniform.name = argText; uniform.pos = uniformPos; uniform.size = uSize; uniform.type = dirParam; uniformPos += uSize; if (*argText != '_') { DVLEData* dvle = GetDvleData(); // Add the uniform to the table if (dvle->uniformCount == MAX_UNIFORM) return throwError("too many referenced uniforms: %s\n", argText); dvle->uniformTable[dvle->uniformCount++] = uniform; // Copy constructor dvle->symbolSize += strlen(argText)+1; } g_aliases.insert( std::pair(argText, uniform.pos | (DEFAULT_OPSRC<<8)) ); #ifdef DEBUG printf("uniform %s[%d] @ d%02X:d%02X\n", argText, uSize, uniform.pos, uniform.pos+uSize-1); #endif } return 0; } DEF_DIRECTIVE(const) { DVLEData* dvle = GetDvleData(); UniformAlloc& alloc = getAlloc(dirParam); NEXT_ARG_CPAREN(constName); NEXT_ARG(arg0Text); NEXT_ARG(arg1Text); NEXT_ARG(arg2Text); char* arg3Text = mystrtok_pos; if (!mystrtok_pos) return missingParam(); char* parenPos = strchr(arg3Text, ')'); if (!parenPos) return throwError("invalid syntax\n"); *parenPos = 0; arg3Text = trim_whitespace(arg3Text); if (g_aliases.find(constName) != g_aliases.end()) return duplicateIdentifier(constName); int uniformPos = alloc.AllocLocal(1); if (uniformPos < 0) return throwError("not enough space for local constant '%s'\n", constName); if (dvle->constantCount == MAX_CONSTANT) return throwError("too many local constants\n"); Constant& ct = dvle->constantTable[dvle->constantCount++]; ct.regId = uniformPos; ct.type = dirParam; if (dirParam == UTYPE_FVEC) { ct.fparam[0] = atof(arg0Text); ct.fparam[1] = atof(arg1Text); ct.fparam[2] = atof(arg2Text); ct.fparam[3] = atof(arg3Text); } else if (dirParam == UTYPE_IVEC) { ct.iparam[0] = atoi(arg0Text) & 0xFF; ct.iparam[1] = atoi(arg1Text) & 0xFF; ct.iparam[2] = atoi(arg2Text) & 0xFF; ct.iparam[3] = atoi(arg3Text) & 0xFF; } g_aliases.insert( std::pair(constName, ct.regId | (DEFAULT_OPSRC<<8)) ); #ifdef DEBUG if (dirParam == UTYPE_FVEC) printf("constant %s(%f, %f, %f, %f) @ d%02X\n", constName, ct.fparam[0], ct.fparam[1], ct.fparam[2], ct.fparam[3], ct.regId); else if (dirParam == UTYPE_IVEC) printf("constant %s(%u, %u, %u, %u) @ d%02X\n", constName, ct.iparam[0], ct.iparam[1], ct.iparam[2], ct.iparam[3], ct.regId); #endif return 0; }; DEF_DIRECTIVE(setfi) { DVLEData* dvle = GetDvleData(); NEXT_ARG_CPAREN(constName); NEXT_ARG(arg0Text); NEXT_ARG(arg1Text); NEXT_ARG(arg2Text); char* arg3Text = mystrtok_pos; if (!mystrtok_pos) return missingParam(); char* parenPos = strchr(arg3Text, ')'); if (!parenPos) return throwError("invalid syntax\n"); *parenPos = 0; arg3Text = trim_whitespace(arg3Text); ARG_TO_REG(constReg, constName); if (dirParam == UTYPE_FVEC) { if (constReg < 0x20 || constReg >= 0x80) return throwError("invalid floating point vector uniform: %s\n", constName); } else if (dirParam == UTYPE_IVEC) { if (constReg < 0x80 || constReg >= 0x84) return throwError("invalid integer vector uniform: %s\n", constName); } if (dvle->constantCount == MAX_CONSTANT) return throwError("too many local constants\n"); Constant& ct = dvle->constantTable[dvle->constantCount++]; ct.regId = constReg; ct.type = dirParam; if (dirParam == UTYPE_FVEC) { ct.fparam[0] = atof(arg0Text); ct.fparam[1] = atof(arg1Text); ct.fparam[2] = atof(arg2Text); ct.fparam[3] = atof(arg3Text); } else if (dirParam == UTYPE_IVEC) { ct.iparam[0] = atoi(arg0Text) & 0xFF; ct.iparam[1] = atoi(arg1Text) & 0xFF; ct.iparam[2] = atoi(arg2Text) & 0xFF; ct.iparam[3] = atoi(arg3Text) & 0xFF; } return 0; } int parseBool(bool& out, const char* text) { if (stricmp(text, "true")==0 || stricmp(text, "on")==0 || stricmp(text, "1")==0) { out = true; return 0; } if (stricmp(text, "false")==0 || stricmp(text, "off")==0 || stricmp(text, "0")==0) { out = false; return 0; } return throwError("invalid bool value: %s\n", text); } DEF_DIRECTIVE(setb) { DVLEData* dvle = GetDvleData(); NEXT_ARG_SPC(constName); NEXT_ARG_SPC(valueText); ENSURE_NO_MORE_ARGS(); ARG_TO_BREG(constReg, constName); bool constVal = false; safe_call(parseBool(constVal, valueText)); if (dvle->constantCount == MAX_CONSTANT) return throwError("too many local constants\n"); Constant& ct = dvle->constantTable[dvle->constantCount++]; ct.regId = constReg; ct.type = UTYPE_BOOL; ct.bparam = constVal; return 0; } static int parseOutType(const char* text) { if (stricmp(text,"pos")==0 || stricmp(text,"position")==0) return OUTTYPE_POS; if (stricmp(text,"nquat")==0 || stricmp(text,"normalquat")==0) return OUTTYPE_NQUAT; if (stricmp(text,"clr")==0 || stricmp(text,"color")==0) return OUTTYPE_CLR; if (stricmp(text,"tcoord0")==0 || stricmp(text,"texcoord0")==0) return OUTTYPE_TCOORD0; if (stricmp(text,"tcoord0w")==0 || stricmp(text,"texcoord0w")==0) return OUTTYPE_TCOORD0W; if (stricmp(text,"tcoord1")==0 || stricmp(text,"texcoord1")==0) return OUTTYPE_TCOORD1; if (stricmp(text,"tcoord2")==0 || stricmp(text,"texcoord2")==0) return OUTTYPE_TCOORD2; if (stricmp(text,"7")==0) return OUTTYPE_7; if (stricmp(text,"view")==0) return OUTTYPE_VIEW; return -1; } DEF_DIRECTIVE(out) { DVLEData* dvle = GetDvleData(); NEXT_ARG_SPC(outName); NEXT_ARG_SPC(outType); ENSURE_NO_MORE_ARGS(); if (!validateIdentifier(outName)) return throwError("invalid identifier: %s\n", outName); int sw = DEFAULT_OPSRC; char* dotPos = strchr(outType, '.'); if (dotPos) { *dotPos++ = 0; sw = parseSwizzling(dotPos); if (sw < 0) return throwError("invalid output mask: %s\n", dotPos); } int mask = maskFromSwizzling(sw, false); int type = parseOutType(outType); if (type < 0) return throwError("invalid output type: %s\n", outType); if (dvle->outputCount==MAX_OUTPUT) return throwError("too many outputs\n"); if (g_aliases.find(outName) != g_aliases.end()) return duplicateIdentifier(outName); int oid = dvle->outputCount; #ifdef DEBUG printf("output %s <- o%d (%d:%X)\n", outName, oid, type, mask); #endif dvle->outputTable[dvle->outputCount++] = OUTPUT_MAKE(type, oid, mask); g_aliases.insert( std::pair(outName, oid | (sw<<8)) ); return 0; } DEF_DIRECTIVE(entry) { DVLEData* dvle = GetDvleData(); NEXT_ARG_SPC(entrypoint); ENSURE_NO_MORE_ARGS(); if (!validateIdentifier(entrypoint)) return throwError("invalid identifier: %s\n", entrypoint); dvle->entrypoint = entrypoint; return 0; } DEF_DIRECTIVE(nodvle) { DVLEData* dvle = GetDvleData(); ENSURE_NO_MORE_ARGS(); if (!dvle->nodvle) { dvle->nodvle = true; g_totalDvleCount --; } } DEF_DIRECTIVE(gsh) { DVLEData* dvle = GetDvleData(); ENSURE_NO_MORE_ARGS(); if (dvle->nodvle) return throwError(".gsh has no effect if .nodvle is used\n"); dvle->isGeoShader = true; return 0; } static const cmdTableType dirTable[] = { DEC_DIRECTIVE(proc), DEC_DIRECTIVE(else), DEC_DIRECTIVE(end), DEC_DIRECTIVE(alias), DEC_DIRECTIVE2(fvec, uniform, UTYPE_FVEC), DEC_DIRECTIVE2(ivec, uniform, UTYPE_IVEC), DEC_DIRECTIVE2(bool, uniform, UTYPE_BOOL), DEC_DIRECTIVE2(constf, const, UTYPE_FVEC), DEC_DIRECTIVE2(consti, const, UTYPE_IVEC), DEC_DIRECTIVE(out), DEC_DIRECTIVE(entry), DEC_DIRECTIVE(nodvle), DEC_DIRECTIVE(gsh), DEC_DIRECTIVE2(setf, setfi, UTYPE_FVEC), DEC_DIRECTIVE2(seti, setfi, UTYPE_IVEC), DEC_DIRECTIVE(setb), { NULL, NULL }, }; int ProcessCommand(const char* cmd) { const cmdTableType* table = cmdTable; if (*cmd == '.') { cmd ++; table = dirTable; } else if (!g_stackPos) return throwError("instruction outside block\n"); for (int i = 0; table[i].name; i ++) if (stricmp(table[i].name, cmd) == 0) return table[i].func(cmd, table[i].opcode, table[i].opcodei); return throwError("invalid instruction: %s\n", cmd); }