diff --git a/drm/shadertoy_sin.fs.inc b/drm/shadertoy_sin.fs.inc index 014fb8c..daaf663 100644 --- a/drm/shadertoy_sin.fs.inc +++ b/drm/shadertoy_sin.fs.inc @@ -20,11 +20,11 @@ 0x00000000, 0x00004000, -0x08020080, +0x10320080, 0x0802f400, 0x00000000, 0x0068c000, -0x20000000, +0x04000000, 0x00004000, 0x08020080, diff --git a/regs/assembler/fs/alu_emitter.py b/regs/assembler/fs/alu_emitter.py new file mode 100644 index 0000000..932ac18 --- /dev/null +++ b/regs/assembler/fs/alu_emitter.py @@ -0,0 +1,115 @@ +from assembler.fs.keywords import KW +from assembler.fs.alu_validator import SrcAddrType, InstructionType +from assembler.fs.common_emitter import US_CMN_INST, US_ALU_RGB_ADDR, US_ALU_ALPHA_ADDR +from assembler.fs.common_emitter import US_ALU_RGB_INST, US_ALU_ALPHA_INST, US_ALU_RGBA_INST + +def emit_alpha_op(code, alpha_op): + # dest + if alpha_op.dest.wmask is not None: + US_CMN_INST.ALPHA_WMASK(code, alpha_op.dest.wmask.value) + if alpha_op.dest.omask is not None: + US_CMN_INST.ALPHA_OMASK(code, alpha_op.dest.omask.value) + assert type(alpha_op.dest.addrd) is int + US_ALU_ALPHA_INST.ALPHA_ADDRD(code, alpha_op.dest.addrd) + + # opcode + US_ALU_ALPHA_INST.ALPHA_OP(code, alpha_op.opcode.value) + + # sels + srcs = [ + US_ALU_ALPHA_INST.ALPHA_SEL_A, + US_ALU_ALPHA_INST.ALPHA_SEL_B, + US_ALU_RGBA_INST.ALPHA_SEL_C, + ] + swizzles = [ + [US_ALU_ALPHA_INST.ALPHA_SWIZ_A], + [US_ALU_ALPHA_INST.ALPHA_SWIZ_B], + [US_ALU_RGBA_INST.ALPHA_SWIZ_C], + ] + mods = [ + US_ALU_ALPHA_INST.ALPHA_MOD_A, + US_ALU_ALPHA_INST.ALPHA_MOD_B, + US_ALU_RGBA_INST.ALPHA_MOD_C, + ] + for sel, src_func, swizzle_funcs, mod_func in zip(alpha_op.sels, + srcs, swizzles, mods): + src_func(code, sel.src.value) + assert len(sel.swizzle) == 1 + assert len(swizzle_funcs) == 1 + for swizzle_func, swizzle in zip(swizzle_funcs, sel.swizzle): + swizzle_func(code, swizzle.value) + mod_func(code, sel.mod.value) + +def emit_rgb_op(code, rgb_op): + # dest + if rgb_op.dest.wmask is not None: + US_CMN_INST.RGB_WMASK(code, rgb_op.dest.wmask.value) + if rgb_op.dest.omask is not None: + US_CMN_INST.RGB_OMASK(code, rgb_op.dest.omask.value) + assert type(rgb_op.dest.addrd) is int + US_ALU_RGBA_INST.RGB_ADDRD(code, rgb_op.dest.addrd) + + # opcode + US_ALU_RGBA_INST.RGB_OP(code, rgb_op.opcode.value) + + # sels + srcs = [ + US_ALU_RGB_INST.RGB_SEL_A, + US_ALU_RGB_INST.RGB_SEL_B, + US_ALU_RGBA_INST.RGB_SEL_C, + ] + swizzles = [ + [US_ALU_RGB_INST.RED_SWIZ_A, US_ALU_RGB_INST.GREEN_SWIZ_A, US_ALU_RGB_INST.BLUE_SWIZ_A], + [US_ALU_RGB_INST.RED_SWIZ_B, US_ALU_RGB_INST.GREEN_SWIZ_B, US_ALU_RGB_INST.BLUE_SWIZ_B], + [US_ALU_RGBA_INST.RED_SWIZ_C, US_ALU_RGBA_INST.GREEN_SWIZ_C, US_ALU_RGBA_INST.BLUE_SWIZ_C], + ] + mods = [ + US_ALU_RGB_INST.RGB_MOD_A, + US_ALU_RGB_INST.RGB_MOD_B, + US_ALU_RGBA_INST.RGB_MOD_C, + ] + for sel, src_func, swizzle_funcs, mod_func in zip(rgb_op.sels, + srcs, swizzles, mods): + src_func(code, sel.src.value) + assert len(sel.swizzle) == 3 + assert len(swizzle_funcs) == 3 + for swizzle_func, swizzle in zip(swizzle_funcs, sel.swizzle): + swizzle_func(code, swizzle.value) + mod_func(code, sel.mod) + +def emit_addr(code, addr): + srcs = [ + (addr.alpha.src0 , US_ALU_ALPHA_ADDR.ADDR0 , US_ALU_ALPHA_ADDR.ADDR0_CONST), + (addr.alpha.src1 , US_ALU_ALPHA_ADDR.ADDR1 , US_ALU_ALPHA_ADDR.ADDR1_CONST), + (addr.alpha.src2 , US_ALU_ALPHA_ADDR.ADDR2 , US_ALU_ALPHA_ADDR.ADDR2_CONST), + (addr.rgb.src0 , US_ALU_RGB_ADDR.ADDR0 , US_ALU_RGB_ADDR.ADDR0_CONST), + (addr.rgb.src1 , US_ALU_RGB_ADDR.ADDR1 , US_ALU_RGB_ADDR.ADDR1_CONST), + (addr.rgb.src2 , US_ALU_RGB_ADDR.ADDR2 , US_ALU_RGB_ADDR.ADDR2_CONST), + ] + + for src, ADDR, ADDR_CONST in srcs: + if src is not None: + is_const = int(src.type is SrcAddrType.const) + is_float = int(src.type is SrcAddrType.float) + ADDR(code, (is_float << 7) | src.value) + ADDR_CONST(code, is_const) + else: + ADDR(code, (1 << 7) | 0) + + if addr.alpha.srcp is not None: + US_ALU_ALPHA_ADDR.SRCP_OP(code, addr.alpha.srcp.value) + if addr.rgb.srcp is not None: + US_ALU_RGB_ADDR.SRCP_OP(code, addr.rgb.srcp.value) + +def emit_instruction(code, ins): + US_CMN_INST.TYPE(code, InstructionType.OUT if KW.OUT in ins.tags else InstructionType.ALU) + US_CMN_INST.TEX_SEM_WAIT(code, int(KW.TEX_SEM_WAIT in ins.tags)) + US_CMN_INST.LAST(code, int(KW.LAST in ins.tags)) + US_CMN_INST.NOP(code, int(KW.NOP in ins.tags)) + US_CMN_INST.ALU_WAIT(code, int(KW.ALU_WAIT in ins.tags)) + + emit_addr(code, ins.addr) + if ins.alpha_op is not None: + emit_alpha_op(code, ins.alpha_op) + if ins.rgb_op is not None: + emit_rgb_op(code, ins.rgb_op) diff --git a/regs/assembler/fs/alu_validator.py b/regs/assembler/fs/alu_validator.py new file mode 100644 index 0000000..3920adb --- /dev/null +++ b/regs/assembler/fs/alu_validator.py @@ -0,0 +1,490 @@ +from pprint import pprint +from dataclasses import dataclass +from enum import Enum, IntEnum, auto +from collections import OrderedDict +from typing import Union + +from assembler.fs.parser import ALUMod +from assembler.fs.keywords import _keyword_to_string, KW +from assembler.error import print_error +from assembler.validator import ValidatorError +from assembler.fs.common_validator import RGBMask, AlphaMask, InstructionType +from assembler.fs.common_validator import validate_identifier_number, validate_dest_keyword, keywords_to_string + +class SrcAddrType(Enum): + temp = auto() + const = auto() + float = auto() + +@dataclass +class SrcAddr: + type: SrcAddrType + value: int + +class SrcMod(IntEnum): + nop = 0 + neg = 1 + abs = 2 + nab = 3 + +class SrcpOp(IntEnum): + neg2 = 0 + sub = 1 + add = 2 + neg = 3 + +@dataclass +class Addr: + src0: SrcAddr = None + src1: SrcAddr = None + src2: SrcAddr = None + srcp: SrcpOp = None + +@dataclass +class AddrRGBAlpha: + alpha: Addr + rgb: Addr + +class Unit(Enum): + alpha = auto() + rgb = auto() + +class RGBOp(IntEnum): + MAD = 0 + DP3 = 1 + DP4 = 2 + D2A = 3 + MIN = 4 + MAX = 5 + CND = 7 + CMP = 8 + FRC = 9 + SOP = 10 + MDH = 11 + MDV = 12 + +class AlphaOp(IntEnum): + MAD = 0 + DP = 1 + MIN = 2 + MAX = 3 + CND = 5 + CMP = 6 + FRC = 7 + EX2 = 8 + LN2 = 9 + RCP = 10 + RSQ = 11 + SIN = 12 + COS = 13 + MDH = 14 + MDV = 15 + +@dataclass +class RGBDest: + addrd: int + wmask: RGBMask + omask: RGBMask + +@dataclass +class AlphaDest: + addrd: int + wmask: AlphaMask + omask: AlphaMask + +class SwizzleSelSrc(IntEnum): + src0 = 0 + src1 = 1 + src2 = 2 + srcp = 3 + +class Swizzle(IntEnum): + r = 0 + g = 1 + b = 2 + a = 3 + zero = 4 + half = 5 + one = 6 + unused = 7 + +@dataclass +class SwizzleSel: + src: SwizzleSelSrc + swizzle: list[Swizzle] + mod: ALUMod + +@dataclass +class AlphaOperation: + dest: AlphaDest + opcode: AlphaOp + sels: list[SwizzleSel] + +@dataclass +class RGBOperation: + dest: RGBDest + opcode: RGBOp + sels: list[SwizzleSel] + +@dataclass +class Instruction: + tags: set[Union[KW.OUT, KW.TEX_SEM_WAIT, KW.NOP]] + addr: Addr + alpha_op: AlphaOperation + rgb_op: RGBOperation + +def validate_instruction_let_expressions(let_expressions): + src_keywords = [KW.SRC0, KW.SRC1, KW.SRC2, KW.SRCP] + src_keyword_strs = keywords_to_string(src_keywords) + rgb_alpha_swizzles = [b"rgb", b"a"] + + addr_rgb_alpha = AddrRGBAlpha(Addr(), Addr()) + + def set_src_by_keyword(addr, keyword, value): + if keyword == KW.SRC0: + addr.src0 = value + elif keyword == KW.SRC1: + addr.src1 = value + elif keyword == KW.SRC2: + addr.src2 = value + elif keyword == KW.SRCP: + addr.srcp = value + else: + assert False, keyword + + def src_value(expr, src): + if src in {KW.SRC0, KW.SRC1, KW.SRC2}: + keyword_to_src_addr_type = OrderedDict([ + (KW.TEMP, SrcAddrType.temp), + (KW.CONST, SrcAddrType.const), + (KW.FLOAT, SrcAddrType.float), + ]) + src_addr_type_strs = keywords_to_string(keyword_to_src_addr_type.keys()) + type_kw = expr.addr_keyword.keyword + if type_kw not in keyword_to_src_addr_type: + raise ValidatorError(f"invalid src addr type, expected one of {src_addr_type_strs}", expr.addr_keyword) + + type = keyword_to_src_addr_type[type_kw] + value = validate_identifier_number(expr.addr_value_identifier) + if type is SrcAddrType.float: + if value >= 128: + raise ValidatorError(f"invalid float value", expr.addr_value_identifier) + elif type is SrcAddrType.temp: + if value >= 128: + raise ValidatorError(f"invalid temp value", expr.addr_value_identifier) + elif type is SrcAddrType.const: + if value >= 256: + raise ValidatorError(f"invalid const value", expr.addr_value_identifier) + else: + assert False, (id(type), id(SrcAddrType.float)) + + return SrcAddr( + type, + value, + ) + elif src == KW.SRCP: + keyword_to_srcp_op = OrderedDict([ + (KW.NEG2, SrcpOp.neg2), + (KW.SUB, SrcpOp.sub), + (KW.ADD, SrcpOp.add), + (KW.NEG, SrcpOp.neg), + ]) + srcp_op_strs = keywords_to_string(keyword_to_srcp_op.keys()) + op = expr.addr_keyword.keyword + if op not in keyword_to_srcp_op: + raise ValidatorError(f"invalid srcp op, expected one of {srcp_op_strs}", expr.addr_keyword) + return keyword_to_srcp_op[op] + else: + assert False, src + + sources = set() + + for expr in let_expressions: + src = expr.src_keyword.keyword + if src not in src_keywords: + raise ValidatorError(f"invalid src keyword, expected one of {src_keyword_strs}", expr.src_keyword) + + src_swizzle = expr.src_swizzle_identifier.lexeme.lower() + if src_swizzle not in rgb_alpha_swizzles: + raise ValidatorError(f"invalid src swizzle, expected one of {rgb_alpha_swizzles}", expr.src_swizzle_identifier) + + source = (_keyword_to_string[src].lower(), src_swizzle) + if source in sources: + raise ValidatorError(f"duplicate source/swizzle in let expressions", expr.src_swizzle_identifier) + sources.add(source) + + value = src_value(expr, src) + + if src_swizzle == b"a": + set_src_by_keyword(addr_rgb_alpha.alpha, src, value) + elif src_swizzle == b"rgb": + set_src_by_keyword(addr_rgb_alpha.rgb, src, value) + else: + assert False, src_swizzle + + return addr_rgb_alpha + +def prevalidate_mask(dest_addr_swizzle, valid_masks): + # we don't know yet whether this is an Alpha operation or an RGB operation + swizzle_str = dest_addr_swizzle.swizzle_identifier.lexeme + if swizzle_str.lower() not in valid_masks: + raise ValidatorError(f"invalid write mask, expected one of {valid_masks}", dest_addr_swizzle.swizzle_identifier) + + mask = swizzle_str.lower() + return mask + +rgb_op_kws = OrderedDict([ + (KW.MAD, RGBOp.MAD), + (KW.DP3, RGBOp.DP3), + (KW.DP4, RGBOp.DP4), + (KW.D2A, RGBOp.D2A), + (KW.MIN, RGBOp.MIN), + (KW.MAX, RGBOp.MAX), + (KW.CND, RGBOp.CND), + (KW.CMP, RGBOp.CMP), + (KW.FRC, RGBOp.FRC), + (KW.SOP, RGBOp.SOP), + (KW.MDH, RGBOp.MDH), + (KW.MDV, RGBOp.MDV) +]) +alpha_op_kws = OrderedDict([ + (KW.MAD, AlphaOp.MAD), + (KW.DP, AlphaOp.DP), + (KW.MIN, AlphaOp.MIN), + (KW.MAX, AlphaOp.MAX), + (KW.CND, AlphaOp.CND), + (KW.CMP, AlphaOp.CMP), + (KW.FRC, AlphaOp.FRC), + (KW.EX2, AlphaOp.EX2), + (KW.LN2, AlphaOp.LN2), + (KW.RCP, AlphaOp.RCP), + (KW.RSQ, AlphaOp.RSQ), + (KW.SIN, AlphaOp.SIN), + (KW.COS, AlphaOp.COS), + (KW.MDH, AlphaOp.MDH), + (KW.MDV, AlphaOp.MDV) +]) + +rgb_masks = OrderedDict([ + (b"none" , RGBMask.NONE), + (b"r" , RGBMask.R), + (b"g" , RGBMask.G), + (b"rg" , RGBMask.RG), + (b"b" , RGBMask.B), + (b"rb" , RGBMask.RB), + (b"gb" , RGBMask.GB), + (b"rgb" , RGBMask.RGB), +]) + +alpha_masks = OrderedDict([ + (b"none" , AlphaMask.NONE), + (b"a" , AlphaMask.A), +]) + +alpha_only_ops = set(alpha_op_kws.keys()) - set(rgb_op_kws.keys()) +rgb_only_ops = set(rgb_op_kws.keys()) - set(alpha_op_kws.keys()) +all_ops = set(rgb_op_kws.keys()) | set(alpha_op_kws.keys()) + +alpha_only_masks = set(alpha_masks.keys()) - set(rgb_masks.keys()) +rgb_only_masks = set(rgb_masks.keys()) - set(alpha_masks.keys()) +all_masks = set(rgb_masks.keys()) | set(alpha_masks.keys()) + +def infer_operation_units(operations): + if len(operations) > 2: + raise ValidatorError("too many operations in instruction", operations[-1].opcode_keyword) + + units = [None, None] + for i, operation in enumerate(operations): + opcode = operation.opcode_keyword.keyword + if opcode not in all_ops: + raise ValidatorError(f"invalid opcode keyword, expected one of {all_ops}", operation.opcode_keyword) + + if len(operation.dest_addr_swizzles) > 2: + raise ValidationError("too many destinations in instruction", operation.dest_addr_swizzles[-1]) + + masks = set(prevalidate_mask(dest_addr_swizzle, all_masks) for dest_addr_swizzle in operation.dest_addr_swizzles) + + def infer_opcode_unit(): + if opcode in alpha_only_ops: + return Unit.alpha + if opcode in rgb_only_ops: + return Unit.rgb + return None + + def infer_mask_unit(): + if any(mask in alpha_only_masks for mask in masks): + return Unit.alpha + if any(mask in rgb_only_masks for mask in masks): + return Unit.rgb + return None + + opcode_unit = infer_opcode_unit() + mask_unit = infer_mask_unit() + if opcode_unit is not None and mask_unit is not None and opcode_unit != mask_unit: + raise ValidatorError(f"contradictory {mask_unit.name} write mask for {opcode_unit.name} opcode", operation.opcode_keyword) + units[i] = opcode_unit or mask_unit + + if units[0] == units[1]: + raise ValidatorError(f"invalid duplicate use of {units[1].name} unit", operations[1].opcode_keyword) + + other_unit = { + Unit.alpha: Unit.rgb, + Unit.rgb: Unit.alpha, + } + + if units[0] is None: + units[0] = other_unit[units[1]] + if units[1] is None: + units[1] = other_unit[units[0]] + + assert units[0] is not None + assert units[1] is not None + assert units[0] != units[1] + + for i, operation in enumerate(operations): + yield units[i], operation + +def validate_instruction_operation_dest(dest_addr_swizzles, mask_lookup, type_cls): + addrs = set() + wmask = None + omask = None + for dest_addr_swizzle in dest_addr_swizzles: + dest = validate_dest_keyword(dest_addr_swizzle.dest_keyword) + addr = validate_identifier_number(dest_addr_swizzle.addr_identifier) + mask = mask_lookup[dest_addr_swizzle.swizzle_identifier.lexeme.lower()] + addrs.add(addr) + if dest == KW.OUT: + omask = mask + elif dest == KW.TEMP: + wmask = mask + else: + assert False, dest + if len(addrs) > 1: + raise ValidatorError(f"too many destination addresses", operation.dest_addr_swizzles[-1].addr_identifier) + addrd, = addrs if addrs else [0] + return type_cls( + addrd=addrd, + wmask=wmask, + omask=omask + ) + +swizzle_sel_src_kws = OrderedDict([ + (KW.SRC0, SwizzleSelSrc.src0), + (KW.SRC1, SwizzleSelSrc.src1), + (KW.SRC2, SwizzleSelSrc.src2), + (KW.SRCP, SwizzleSelSrc.srcp), +]) + +swizzle_kws = OrderedDict([ + (ord("r"), Swizzle.r), + (ord("g"), Swizzle.g), + (ord("b"), Swizzle.b), + (ord("a"), Swizzle.a), + (ord("0"), Swizzle.zero), + (ord("h"), Swizzle.half), + (ord("1"), Swizzle.one), + (ord("_"), Swizzle.unused), +]) + +def validate_instruction_operation_sels(swizzle_sels, is_alpha): + if len(swizzle_sels) > 3: + raise ValidatorError("too many swizzle sels", swizzle_sels[-1].sel_keyword) + + sels = [] + for swizzle_sel in swizzle_sels: + if swizzle_sel.sel_keyword.keyword not in swizzle_sel_src_kws: + raise ValidatorError("invalid swizzle src", swizzle_sel.sel_keyword.keyword) + src = swizzle_sel_src_kws[swizzle_sel.sel_keyword.keyword] + + swizzle_lexeme = swizzle_sel.swizzle_identifier.lexeme.lower() + swizzles_length = 1 if is_alpha else 3 + if len(swizzle_lexeme) != swizzles_length: + raise ValidatorError("invalid swizzle length", swizzle_sel.swizzle_identifier) + if not all(c in swizzle_kws for c in swizzle_lexeme): + raise ValidatorError("invalid swizzle characters", swizzle_sel.swizzle_identifier) + swizzle = [ + swizzle_kws[c] for c in swizzle_lexeme + ] + + mod = swizzle_sel.mod + sels.append(SwizzleSel(src, swizzle, mod)) + return sels + +def validate_alpha_instruction_operation(operation): + dest = validate_instruction_operation_dest(operation.dest_addr_swizzles, + mask_lookup=alpha_masks, + type_cls=AlphaDest) + opcode = alpha_op_kws[operation.opcode_keyword.keyword] + sels = validate_instruction_operation_sels(operation.swizzle_sels, is_alpha=True) + return AlphaOperation( + dest, + opcode, + sels + ) + +def validate_rgb_instruction_operation(operation): + dest = validate_instruction_operation_dest(operation.dest_addr_swizzles, + mask_lookup=rgb_masks, + type_cls=RGBDest) + opcode = rgb_op_kws[operation.opcode_keyword.keyword] + sels = validate_instruction_operation_sels(operation.swizzle_sels, is_alpha=False) + return RGBOperation( + dest, + opcode, + sels + ) + +def validate_instruction_operations(operations): + for unit, operation in infer_operation_units(operations): + if unit is Unit.alpha: + yield validate_alpha_instruction_operation(operation) + elif unit is Unit.rgb: + yield validate_rgb_instruction_operation(operation) + else: + assert False, unit + +def validate_instruction(ins): + addr_rgb_alpha = validate_instruction_let_expressions(ins.let_expressions) + + tags = set([tag.keyword for tag in ins.tags]) + + instruction = Instruction( + tags, + addr_rgb_alpha, + None, + None + ) + for op in validate_instruction_operations(ins.operations): + if type(op) is RGBOperation: + instruction.rgb_op = op + elif type(op) is AlphaOperation: + instruction.alpha_op = op + else: + assert False, op + return instruction + +if __name__ == "__main__": + from assembler.lexer import Lexer, LexerError + from assembler.fs.parser import Parser, ParserError + from assembler.fs.keywords import find_keyword + + buf = b""" +src0.a = float(0), src0.rgb = temp[0] , srcp.a = neg : + out[0].none = temp[0].none = MAD src0.r src0.r src0.r , + out[0].none = temp[0].r = DP3 src0.rg0 src0.rg0 ; + """ + lexer = Lexer(buf, find_keyword, emit_newlines=False, minus_is_token=True) + tokens = list(lexer.lex_tokens()) + parser = Parser(tokens) + try: + ins_ast = parser.instruction() + pprint(validate_instruction(ins_ast)) + except LexerError as e: + print_error(None, buf, e) + raise + except ParserError as e: + print_error(None, buf, e) + raise + except ValidatorError as e: + print_error(None, buf, e) + raise diff --git a/regs/assembler/fs/common_emitter.py b/regs/assembler/fs/common_emitter.py new file mode 100644 index 0000000..f13f182 --- /dev/null +++ b/regs/assembler/fs/common_emitter.py @@ -0,0 +1,69 @@ +from os import path +from functools import partial + +import parse_bits + +class BaseRegister: + def set(self, code, value, *, code_ix, descriptor): + if type(descriptor.bits) is int: + mask = 1 + low = descriptor.bits + else: + high, low = descriptor.bits + assert high > low + mask_length = (high - low) + 1 + mask = (1 << mask_length) - 1 + + code_value = code[code_ix] + assert (code_value >> low) & mask == 0 + assert value & mask == value + code[code_ix] |= (value & mask) << low + +_descriptor_indicies = { + "US_CMN_INST": 0, + "US_ALU_RGB_ADDR": 1, + "US_ALU_ALPHA_ADDR": 2, + "US_ALU_RGB_INST": 3, + "US_ALU_ALPHA_INST": 4, + "US_ALU_RGBA_INST": 5, + + "US_TEX_INST": 1, + "US_TEX_ADDR": 2, + "US_TEX_ADDR_DXDY": 3, + + "US_FC_INST": 2, + "US_FC_ADDR": 3, +} + +def parse_register(register_name): + base = path.dirname(__file__) + + filename = path.join(base, "..", "..", "bits", register_name.lower() + ".txt") + l = list(parse_bits.parse_file_fields(filename)) + cls = type(register_name, (BaseRegister,), {}) + instance = cls() + descriptors = list(parse_bits.aggregate(l)) + code_ix = _descriptor_indicies[register_name] + for descriptor in descriptors: + setattr(instance, descriptor.field_name, + partial(instance.set, code_ix=code_ix, descriptor=descriptor)) + func = getattr(instance, descriptor.field_name) + for pv_value, (pv_name, _) in descriptor.possible_values.items(): + if pv_name is not None: + setattr(func, pv_name, pv_value) + assert getattr(instance, "descriptors", None) is None + instance.descriptors = descriptors + + return instance + +US_CMN_INST = parse_register("US_CMN_INST") +US_ALU_RGB_ADDR = parse_register("US_ALU_RGB_ADDR") +US_ALU_ALPHA_ADDR = parse_register("US_ALU_ALPHA_ADDR") +US_ALU_RGB_INST = parse_register("US_ALU_RGB_INST") +US_ALU_ALPHA_INST = parse_register("US_ALU_ALPHA_INST") +US_ALU_RGBA_INST = parse_register("US_ALU_RGBA_INST") +US_TEX_INST = parse_register("US_TEX_INST") +US_TEX_ADDR = parse_register("US_TEX_ADDR") +US_TEX_ADDR_DXDY = parse_register("US_TEX_ADDR_DXDY") +US_FC_INST = parse_register("US_FC_INST") +US_FC_ADDR = parse_register("US_FC_ADDR") diff --git a/regs/assembler/fs/common_validator.py b/regs/assembler/fs/common_validator.py new file mode 100644 index 0000000..02c44c1 --- /dev/null +++ b/regs/assembler/fs/common_validator.py @@ -0,0 +1,43 @@ +from enum import IntEnum +from collections import OrderedDict + +from assembler.fs.keywords import _keyword_to_string +from assembler.fs.keywords import KW +from assembler.validator import ValidatorError + +class InstructionType(IntEnum): + ALU = 0 + OUT = 1 + FC = 2 + TEX = 3 + +class RGBMask(IntEnum): + NONE = 0 + R = 1 + G = 2 + RG = 3 + B = 4 + RB = 5 + GB = 6 + RGB = 7 + +class AlphaMask(IntEnum): + NONE = 0 + A = 1 + +def validate_identifier_number(token): + try: + return int(token.lexeme, 10) + except ValueError: + raise ValidatorError("invalid number", token) + +def validate_dest_keyword(dest_keyword): + dest_keywords = [KW.OUT, KW.TEMP] + dest_keyword_strs = keywords_to_string(dest_keywords) + dest = dest_keyword.keyword + if dest not in dest_keywords: + raise ValidatorError(f"invalid dest keyword, expected one of {dest_keyword_strs}", dest_addr_swizzle.dest_keyword) + return dest + +def keywords_to_string(keywords): + return [_keyword_to_string[s].decode("utf-8") for s in keywords] diff --git a/regs/assembler/fs/emitter.py b/regs/assembler/fs/emitter.py index f8e74e5..4369332 100644 --- a/regs/assembler/fs/emitter.py +++ b/regs/assembler/fs/emitter.py @@ -1,180 +1,12 @@ -from os import path -from pprint import pprint -from functools import partial - -import parse_bits -from assembler.fs.validator import SrcAddrType - -class BaseRegister: - def set(self, code, value, *, code_ix, descriptor): - if type(descriptor.bits) is int: - mask = 1 - low = descriptor.bits - else: - high, low = descriptor.bits - assert high > low - mask_length = (high - low) + 1 - mask = (1 << mask_length) - 1 - - code_value = code[code_ix] - assert (code_value >> low) & mask == 0 - assert value & mask == value - code[code_ix] |= (value & mask) << low - -_descriptor_indicies = { - "US_CMN_INST": 0, - "US_ALU_RGB_ADDR": 1, - "US_ALU_ALPHA_ADDR": 2, - "US_ALU_RGB_INST": 3, - "US_ALU_ALPHA_INST": 4, - "US_ALU_RGBA_INST": 5, - - "US_TEX_INST": 1, - "US_TEX_ADDR": 2, - "US_TEX_ADDR_DXDY": 3, - - "US_FC_INST": 2, - "US_FC_ADDR": 3, -} - -def parse_register(register_name): - base = path.dirname(__file__) - - filename = path.join(base, "..", "..", "bits", register_name.lower() + ".txt") - l = list(parse_bits.parse_file_fields(filename)) - cls = type(register_name, (BaseRegister,), {}) - instance = cls() - descriptors = list(parse_bits.aggregate(l)) - code_ix = _descriptor_indicies[register_name] - for descriptor in descriptors: - setattr(instance, descriptor.field_name, - partial(instance.set, code_ix=code_ix, descriptor=descriptor)) - func = getattr(instance, descriptor.field_name) - for pv_value, (pv_name, _) in descriptor.possible_values.items(): - if pv_name is not None: - setattr(func, pv_name, pv_value) - assert getattr(instance, "descriptors", None) is None - instance.descriptors = descriptors - - return instance - -US_CMN_INST = parse_register("US_CMN_INST") -US_ALU_RGB_ADDR = parse_register("US_ALU_RGB_ADDR") -US_ALU_ALPHA_ADDR = parse_register("US_ALU_ALPHA_ADDR") -US_ALU_RGB_INST = parse_register("US_ALU_RGB_INST") -US_ALU_ALPHA_INST = parse_register("US_ALU_ALPHA_INST") -US_ALU_RGBA_INST = parse_register("US_ALU_RGBA_INST") -US_TEX_INST = parse_register("US_TEX_INST") -US_TEX_ADDR = parse_register("US_TEX_ADDR") -US_TEX_ADDR_DXDY = parse_register("US_TEX_ADDR_DXDY") -US_FC_INST = parse_register("US_FC_INST") -US_FC_ADDR = parse_register("US_FC_ADDR") - -def emit_alpha_op(code, alpha_op): - # dest - if alpha_op.dest.wmask is not None: - US_CMN_INST.ALPHA_WMASK(code, alpha_op.dest.wmask.value) - if alpha_op.dest.omask is not None: - US_CMN_INST.ALPHA_OMASK(code, alpha_op.dest.omask.value) - assert type(alpha_op.dest.addrd) is int - US_ALU_ALPHA_INST.ALPHA_ADDRD(code, alpha_op.dest.addrd) - - # opcode - US_ALU_ALPHA_INST.ALPHA_OP(code, alpha_op.opcode.value) - - # sels - srcs = [ - US_ALU_ALPHA_INST.ALPHA_SEL_A, - US_ALU_ALPHA_INST.ALPHA_SEL_B, - US_ALU_RGBA_INST.ALPHA_SEL_C, - ] - swizzles = [ - [US_ALU_ALPHA_INST.ALPHA_SWIZ_A], - [US_ALU_ALPHA_INST.ALPHA_SWIZ_B], - [US_ALU_RGBA_INST.ALPHA_SWIZ_C], - ] - mods = [ - US_ALU_ALPHA_INST.ALPHA_MOD_A, - US_ALU_ALPHA_INST.ALPHA_MOD_B, - US_ALU_RGBA_INST.ALPHA_MOD_C, - ] - for sel, src_func, swizzle_funcs, mod_func in zip(alpha_op.sels, - srcs, swizzles, mods): - src_func(code, sel.src.value) - assert len(sel.swizzle) == 1 - assert len(swizzle_funcs) == 1 - for swizzle_func, swizzle in zip(swizzle_funcs, sel.swizzle): - swizzle_func(code, swizzle.value) - mod_func(code, sel.mod.value) - -def emit_rgb_op(code, rgb_op): - # dest - if rgb_op.dest.wmask is not None: - US_CMN_INST.RGB_WMASK(code, rgb_op.dest.wmask.value) - if rgb_op.dest.omask is not None: - US_CMN_INST.RGB_OMASK(code, rgb_op.dest.omask.value) - assert type(rgb_op.dest.addrd) is int - US_ALU_RGBA_INST.RGB_ADDRD(code, rgb_op.dest.addrd) - - # opcode - US_ALU_RGBA_INST.RGB_OP(code, rgb_op.opcode.value) - - # sels - srcs = [ - US_ALU_RGB_INST.RGB_SEL_A, - US_ALU_RGB_INST.RGB_SEL_B, - US_ALU_RGBA_INST.RGB_SEL_C, - ] - swizzles = [ - [US_ALU_RGB_INST.RED_SWIZ_A, US_ALU_RGB_INST.GREEN_SWIZ_A, US_ALU_RGB_INST.BLUE_SWIZ_A], - [US_ALU_RGB_INST.RED_SWIZ_B, US_ALU_RGB_INST.GREEN_SWIZ_B, US_ALU_RGB_INST.BLUE_SWIZ_B], - [US_ALU_RGBA_INST.RED_SWIZ_C, US_ALU_RGBA_INST.GREEN_SWIZ_C, US_ALU_RGBA_INST.BLUE_SWIZ_C], - ] - mods = [ - US_ALU_RGB_INST.RGB_MOD_A, - US_ALU_RGB_INST.RGB_MOD_B, - US_ALU_RGBA_INST.RGB_MOD_C, - ] - for sel, src_func, swizzle_funcs, mod_func in zip(rgb_op.sels, - srcs, swizzles, mods): - src_func(code, sel.src.value) - assert len(sel.swizzle) == 3 - assert len(swizzle_funcs) == 3 - for swizzle_func, swizzle in zip(swizzle_funcs, sel.swizzle): - swizzle_func(code, swizzle.value) - mod_func(code, sel.mod) - -def emit_addr(code, addr): - srcs = [ - (addr.alpha.src0 , US_ALU_ALPHA_ADDR.ADDR0 , US_ALU_ALPHA_ADDR.ADDR0_CONST), - (addr.alpha.src1 , US_ALU_ALPHA_ADDR.ADDR1 , US_ALU_ALPHA_ADDR.ADDR1_CONST), - (addr.alpha.src2 , US_ALU_ALPHA_ADDR.ADDR2 , US_ALU_ALPHA_ADDR.ADDR2_CONST), - (addr.rgb.src0 , US_ALU_RGB_ADDR.ADDR0 , US_ALU_RGB_ADDR.ADDR0_CONST), - (addr.rgb.src1 , US_ALU_RGB_ADDR.ADDR1 , US_ALU_RGB_ADDR.ADDR1_CONST), - (addr.rgb.src2 , US_ALU_RGB_ADDR.ADDR2 , US_ALU_RGB_ADDR.ADDR2_CONST), - ] - - for src, ADDR, ADDR_CONST in srcs: - if src is not None: - is_const = int(src.type is SrcAddrType.const) - is_float = int(src.type is SrcAddrType.float) - ADDR(code, (is_float << 7) | src.value) - ADDR_CONST(code, is_const) - else: - ADDR(code, (1 << 7) | 0) - - if addr.alpha.srcp is not None: - US_ALU_ALPHA_ADDR.SRCP_OP(code, addr.alpha.srcp.value) - if addr.rgb.srcp is not None: - US_ALU_RGB_ADDR.SRCP_OP(code, addr.rgb.srcp.value) +from assembler.fs import alu_emitter +from assembler.fs import tex_emitter +from assembler.fs import alu_validator +from assembler.fs import tex_validator def emit_instruction(code, ins): - US_CMN_INST.TYPE(code, ins.type.value) - US_CMN_INST.TEX_SEM_WAIT(code, int(ins.tex_sem_wait)) - US_CMN_INST.NOP(code, int(ins.nop)) - - emit_addr(code, ins.addr) - if ins.alpha_op is not None: - emit_alpha_op(code, ins.alpha_op) - if ins.rgb_op is not None: - emit_rgb_op(code, ins.rgb_op) + if type(ins) is alu_validator.Instruction: + return alu_emitter.emit_instruction(code, ins) + elif type(ins) is tex_validator.Instruction: + return tex_emitter.emit_instruction(code, ins) + else: + assert False, type(ins) diff --git a/regs/assembler/fs/keywords.py b/regs/assembler/fs/keywords.py index d47a4f4..1b46384 100644 --- a/regs/assembler/fs/keywords.py +++ b/regs/assembler/fs/keywords.py @@ -41,6 +41,18 @@ class KW(Enum): # modifiers NOP = auto() TEX_SEM_WAIT = auto() + TEX = auto() + TEX_SEM_ACQUIRE = auto() + ALU_WAIT = auto() + LAST = auto() + + # tex opcodes + LD = auto() + TEXKILL = auto() + PROJ = auto() + LODBIAS = auto() + LOD = auto() + DXDY = auto() _string_to_keyword = { b"CMP": KW.CMP, @@ -76,6 +88,16 @@ _string_to_keyword = { b"NEG": KW.NEG, b"NOP": KW.NOP, b"TEX_SEM_WAIT": KW.TEX_SEM_WAIT, + b"TEX": KW.TEX, + b"TEX_SEM_ACQUIRE": KW.TEX_SEM_ACQUIRE, + b"ALU_WAIT": KW.ALU_WAIT, + b"LAST": KW.LAST, + b"LD": KW.LD, + b"TEXKILL": KW.TEXKILL, + b"PROJ": KW.PROJ, + b"LODBIAS": KW.LODBIAS, + b"LOD": KW.LOD, + b"DXDY": KW.DXDY, } _keyword_to_string = {v:k for k,v in _string_to_keyword.items()} diff --git a/regs/assembler/fs/parser.py b/regs/assembler/fs/parser.py index 13027e5..d854502 100644 --- a/regs/assembler/fs/parser.py +++ b/regs/assembler/fs/parser.py @@ -7,14 +7,14 @@ from assembler.lexer import TT, Token from assembler.fs.keywords import KW, find_keyword from assembler.error import print_error -class Mod(IntEnum): +class ALUMod(IntEnum): nop = 0 neg = 1 abs = 2 nab = 3 @dataclass -class LetExpression: +class ALULetExpression: src_keyword: Token src_swizzle_identifier: Token addr_keyword: Token @@ -27,27 +27,39 @@ class DestAddrSwizzle: swizzle_identifier: Token @dataclass -class SwizzleSel: +class ALUSwizzleSel: sel_keyword: Token swizzle_identifier: Token - mod: Mod + mod: ALUMod @dataclass -class Operation: +class ALUOperation: dest_addr_swizzles: list[DestAddrSwizzle] opcode_keyword: Token - swizzle_sels: list[SwizzleSel] + swizzle_sels: list[ALUSwizzleSel] @dataclass -class Instruction: - out: bool - tex_sem_wait: bool - nop: bool - let_expressions: list[LetExpression] - operations: list[Operation] +class ALUInstruction: + tags: set[Token] + let_expressions: list[ALULetExpression] + operations: list[ALUOperation] + +@dataclass +class TEXOperation: + dest_addr_swizzles: list[DestAddrSwizzle] + opcode_keyword: Token + tex_id_identifier: Token + tex_dst_swizzle_identifier: Token + tex_src_address_identifier: Token + tex_src_swizzle_identifier: Token + +@dataclass +class TEXInstruction: + tags: set[Token] + operation: TEXOperation class Parser(BaseParser): - def let_expression(self): + def alu_let_expression(self): src_keyword = self.consume(TT.keyword, "expected src keyword") self.consume(TT.dot, "expected dot") src_swizzle_identifier = self.consume(TT.identifier, "expected src swizzle identifier") @@ -69,7 +81,7 @@ class Parser(BaseParser): else: self.consume(TT.right_square, "expected right square") - return LetExpression( + return ALULetExpression( src_keyword, src_swizzle_identifier, addr_keyword, @@ -90,7 +102,7 @@ class Parser(BaseParser): swizzle_identifier, ) - def is_opcode(self): + def alu_is_opcode(self): opcode_keywords = { KW.CMP, KW.CND, KW.COS, KW.D2A, KW.DP , KW.DP3, KW.DP4, KW.EX2, @@ -103,21 +115,21 @@ class Parser(BaseParser): return token.keyword in opcode_keywords return False - def is_neg(self): + def alu_is_neg(self): result = self.match(TT.minus) if result: self.advance() return result - def is_abs(self): + def alu_is_abs(self): result = self.match(TT.bar) if result: self.advance() return result - def swizzle_sel(self): - neg = self.is_neg() - abs = self.is_abs() + def alu_swizzle_sel(self): + neg = self.alu_is_neg() + abs = self.alu_is_abs() sel_keyword = self.consume(TT.keyword, "expected sel keyword") self.consume(TT.dot, "expected dot") @@ -128,52 +140,51 @@ class Parser(BaseParser): mod_table = { # (neg, abs) - (False, False): Mod.nop, - (False, True): Mod.abs, - (True, False): Mod.neg, - (True, True): Mod.nab, + (False, False): ALUMod.nop, + (False, True): ALUMod.abs, + (True, False): ALUMod.neg, + (True, True): ALUMod.nab, } mod = mod_table[(neg, abs)] - return SwizzleSel( + return ALUSwizzleSel( sel_keyword, swizzle_identifier, mod, ) - def operation(self): + def alu_operation(self): dest_addr_swizzles = [] - while not self.is_opcode(): + while not self.alu_is_opcode(): dest_addr_swizzles.append(self.dest_addr_swizzle()) opcode_keyword = self.consume(TT.keyword, "expected opcode keyword") swizzle_sels = [] while not (self.match(TT.comma) or self.match(TT.semicolon)): - swizzle_sels.append(self.swizzle_sel()) + swizzle_sels.append(self.alu_swizzle_sel()) - return Operation( + return ALUOperation( dest_addr_swizzles, opcode_keyword, swizzle_sels ) - def instruction(self): - out = False - if self.match_keyword(KW.OUT): - self.advance() - out = True - tex_sem_wait = False - if self.match_keyword(KW.TEX_SEM_WAIT): - self.advance() - tex_sem_wait = True - nop = False - if self.match_keyword(KW.NOP): - self.advance() - nop = True + def alu_instruction(self, out: bool): + tags = list() + tag_keywords = set([KW.OUT, KW.TEX_SEM_WAIT, KW.NOP, KW.LAST, KW.ALU_WAIT]) + while True: + if self.match(TT.keyword): + token = self.peek() + if token.keyword in tag_keywords: + self.advance() + tag_keywords.remove(token.keyword) + tags.append(token) + continue + break let_expressions = [] while not self.match(TT.colon): - let_expressions.append(self.let_expression()) + let_expressions.append(self.alu_let_expression()) if not self.match(TT.colon): self.consume(TT.comma, "expected comma") @@ -181,20 +192,87 @@ class Parser(BaseParser): operations = [] while not self.match(TT.semicolon): - operations.append(self.operation()) + operations.append(self.alu_operation()) if not self.match(TT.semicolon): self.consume(TT.comma, "expected comma") self.consume(TT.semicolon, "expected semicolon") - return Instruction( - out, - tex_sem_wait, - nop, + return ALUInstruction( + tags, let_expressions, operations, ) + def tex_is_opcode(self): + opcode_keywords = { + KW.NOP, KW.LD, KW.TEXKILL, KW.PROJ, + KW.LODBIAS, KW.LOD, KW.DXDY, + } + if self.match(TT.keyword): + token = self.peek() + return token.keyword in opcode_keywords + return False + + def tex_operation(self): + dest_addr_swizzles = [] + while not self.tex_is_opcode(): + dest_addr_swizzles.append(self.dest_addr_swizzle()) + opcode_keyword = self.consume(TT.keyword, "expected tex opcode keyword") + + self.consume_keyword(KW.TEX, "expected tex keyword") + self.consume(TT.left_square, "expected left square") + tex_id_identifier = self.consume(TT.identifier, "expected tex ID identifier") + self.consume(TT.right_square, "expected left square") + self.consume(TT.dot, "expected dot") + tex_dst_swizzle_identifier = self.consume(TT.identifier, "expected src swizzle") + + self.consume_keyword(KW.TEMP, "expected temp keyword") + self.consume(TT.left_square, "expected left square") + tex_src_address_identifier = self.consume(TT.identifier, "expected tex address identifier") + self.consume(TT.right_square, "expected left square") + self.consume(TT.dot, "expected dot") + tex_src_swizzle_identifier = self.consume(TT.identifier, "expected dst swizzle") + + return TEXOperation( + dest_addr_swizzles, + opcode_keyword, + tex_id_identifier, + tex_dst_swizzle_identifier, + tex_src_address_identifier, + tex_src_swizzle_identifier, + ) + + def tex_instruction(self): + tags = list() + tag_keywords = set([KW.OUT, KW.TEX_SEM_WAIT, KW.TEX_SEM_ACQUIRE, KW.NOP, KW.ALU_WAIT, KW.LAST]) + while True: + if self.match(TT.keyword): + token = self.peek() + if token.keyword in tag_keywords: + self.advance() + tag_keywords.remove(token.keyword) + tags.append(token) + continue + break + + operation = self.tex_operation() + + self.consume(TT.semicolon, "expected semicolon") + + return TEXInstruction( + tags, + operation, + ) + + def instruction(self): + out = False + if self.match_keyword(KW.TEX): + self.advance() + return self.tex_instruction() + else: + return self.alu_instruction(out=False) + def instructions(self): while not self.match(TT.eof): yield self.instruction() diff --git a/regs/assembler/fs/tex_emitter.py b/regs/assembler/fs/tex_emitter.py new file mode 100644 index 0000000..8defe1f --- /dev/null +++ b/regs/assembler/fs/tex_emitter.py @@ -0,0 +1,36 @@ +from assembler.fs.common_validator import InstructionType +from assembler.fs.keywords import KW + +from assembler.fs.common_emitter import US_CMN_INST +from assembler.fs.common_emitter import US_TEX_INST +from assembler.fs.common_emitter import US_TEX_ADDR +from assembler.fs.common_emitter import US_TEX_ADDR_DXDY + +def emit_instruction(code, ins): + US_CMN_INST.TYPE(code, InstructionType.TEX) + US_CMN_INST.TEX_SEM_WAIT(code, int(KW.TEX_SEM_WAIT in ins.tags)) + US_CMN_INST.LAST(code, int(KW.LAST in ins.tags)) + US_CMN_INST.NOP(code, int(KW.NOP in ins.tags)) + US_CMN_INST.ALU_WAIT(code, int(KW.ALU_WAIT in ins.tags)) + US_CMN_INST.RGB_WMASK(code, ins.masks.rgb_wmask.value) + US_CMN_INST.ALPHA_WMASK(code, ins.masks.alpha_wmask.value) + US_CMN_INST.RGB_OMASK(code, ins.masks.rgb_omask.value) + US_CMN_INST.ALPHA_OMASK(code, ins.masks.alpha_omask.value) + + US_TEX_INST.TEX_ID(code, ins.tex_id) + US_TEX_INST.INST(code, ins.opcode.value) + US_TEX_INST.TEX_SEM_ACQUIRE(code, int(KW.TEX_SEM_ACQUIRE in ins.tags)) + US_TEX_INST.IGNORE_UNCOVERED(code, 0) + US_TEX_INST.UNSCALED(code, 0) + + US_TEX_ADDR.SRC_ADDR(code, ins.src_addr) + US_TEX_ADDR.SRC_S_SWIZ(code, ins.src_swizzle[0].value) + US_TEX_ADDR.SRC_T_SWIZ(code, ins.src_swizzle[1].value) + US_TEX_ADDR.SRC_R_SWIZ(code, ins.src_swizzle[2].value) + US_TEX_ADDR.SRC_Q_SWIZ(code, ins.src_swizzle[3].value) + + US_TEX_ADDR.DST_ADDR(code, ins.dst_addr) + US_TEX_ADDR.DST_R_SWIZ(code, ins.dst_swizzle[0].value) + US_TEX_ADDR.DST_G_SWIZ(code, ins.dst_swizzle[1].value) + US_TEX_ADDR.DST_B_SWIZ(code, ins.dst_swizzle[2].value) + US_TEX_ADDR.DST_A_SWIZ(code, ins.dst_swizzle[3].value) diff --git a/regs/assembler/fs/tex_validator.py b/regs/assembler/fs/tex_validator.py new file mode 100644 index 0000000..d81bf05 --- /dev/null +++ b/regs/assembler/fs/tex_validator.py @@ -0,0 +1,159 @@ +from dataclasses import dataclass +from typing import Union +from collections import OrderedDict +from enum import IntEnum + +from assembler.validator import ValidatorError +from assembler.fs import parser +from assembler.fs.keywords import KW +from assembler.fs.common_validator import RGBMask, AlphaMask +from assembler.fs.common_validator import validate_identifier_number, validate_dest_keyword +from assembler.fs.common_validator import keywords_to_string + +@dataclass +class Masks: + alpha_wmask: AlphaMask + rgb_wmask: RGBMask + alpha_omask: AlphaMask + rgb_omask: RGBMask + +class TEXOp(IntEnum): + NOP = 0 + LD = 1 + TEXKILL = 2 + PROJ = 3 + LODBIAS = 4 + LOD = 5 + DXDY = 6 + +class Swizzle(IntEnum): + R = 0 + G = 1 + B = 2 + A = 3 + +@dataclass +class Instruction: + tags: set[Union[KW.TEX_SEM_WAIT, KW.TEX_SEM_ACQUIRE, KW.OUT]] + masks: Masks + opcode: TEXOp + tex_id: int + src_addr: int + src_swizzle: tuple[Swizzle, Swizzle, Swizzle, Swizzle] + dst_addr: int + dst_swizzle: tuple[Swizzle, Swizzle, Swizzle, Swizzle] + +def validate_swizzle(token): + if len(token.lexeme) != 4: + raise ValidatorError("invalid swizzle identifier", token) + swizzles = OrderedDict([ + (ord(b"r"), Swizzle.R), + (ord(b"g"), Swizzle.G), + (ord(b"b"), Swizzle.B), + (ord(b"a"), Swizzle.A), + ]) + if not all(c in swizzles for c in token.lexeme): + raise ValidatorError("invalid swizzle identifier", token) + return tuple(swizzles[c] for c in token.lexeme) + +def validate_mask_swizzle(token) -> tuple[AlphaMask, RGBMask]: + argb_masks = OrderedDict([ + (b"none" , (AlphaMask.NONE, RGBMask.NONE)), + (b"r" , (AlphaMask.NONE, RGBMask.R)), + (b"g" , (AlphaMask.NONE, RGBMask.G)), + (b"rg" , (AlphaMask.NONE, RGBMask.RG)), + (b"b" , (AlphaMask.NONE, RGBMask.B)), + (b"rb" , (AlphaMask.NONE, RGBMask.RB)), + (b"gb" , (AlphaMask.NONE, RGBMask.GB)), + (b"rgb" , (AlphaMask.NONE, RGBMask.RGB)), + (b"ar" , (AlphaMask.A, RGBMask.R)), + (b"ag" , (AlphaMask.A, RGBMask.G)), + (b"arg" , (AlphaMask.A, RGBMask.RG)), + (b"ab" , (AlphaMask.A, RGBMask.B)), + (b"arb" , (AlphaMask.A, RGBMask.RB)), + (b"agb" , (AlphaMask.A, RGBMask.GB)), + (b"argb" , (AlphaMask.A, RGBMask.RGB)), + ]) + if token.lexeme not in argb_masks: + raise ValidatorError("invalid destination mask", token) + return argb_masks[token.lexeme] + +def validate_masks(ins_ast: parser.TEXInstruction): + addresses = set() + dests = set() + + masks = Masks( + alpha_wmask = AlphaMask.NONE, + rgb_wmask = RGBMask.NONE, + alpha_omask = AlphaMask.NONE, + rgb_omask = RGBMask.NONE, + ) + + for dest_addr_swizzle in ins_ast.operation.dest_addr_swizzles: + dest_keyword = dest_addr_swizzle.dest_keyword + dest = validate_dest_keyword(dest_keyword) + if dest in dests: + raise ValidatorError(f"duplicate destination keyword {dest}", dest_keyword) + dests.add(dest) + + addr_identifier = dest_addr_swizzle.addr_identifier + addr = validate_identifier_number(addr_identifier) + addresses.add(addr) + + swizzle_identifier = dest_addr_swizzle.swizzle_identifier + alpha_mask, rgb_mask = validate_mask_swizzle(swizzle_identifier) + + if dest is KW.OUT: + masks.alpha_omask = alpha_mask + masks.rgb_omask = rgb_mask + elif dest is KW.TEMP: + masks.alpha_wmask = alpha_mask + masks.rgb_wmask = rgb_mask + else: + assert False, type(dest) + + if len(addresses) > 1: + raise ValidatorError("contradictory destination address", ins_ast.operation.dest_addr_swizzles[-1].addr_identifier) + + address, = addresses + + return masks, address + +op_kws = OrderedDict([ + (KW.NOP, TEXOp.NOP), + (KW.LD, TEXOp.LD), + (KW.TEXKILL, TEXOp.TEXKILL), + (KW.PROJ, TEXOp.PROJ), + (KW.LODBIAS, TEXOp.LODBIAS), + (KW.LOD, TEXOp.LOD), + (KW.DXDY, TEXOp.DXDY), +]) + +def validate_opcode(token): + if token.keyword not in op_kws: + raise ValidatorError("invalid tex opcode keyword", token) + return op_kws[token.keyword] + +def validate_instruction(ins_ast: parser.TEXInstruction): + tags = set([tag.keyword for tag in ins_ast.tags]) + + masks, dst_addr = validate_masks(ins_ast) + opcode = validate_opcode(ins_ast.operation.opcode_keyword) + + tex_id = validate_identifier_number(ins_ast.operation.tex_id_identifier) + + dst_swizzle = validate_swizzle(ins_ast.operation.tex_dst_swizzle_identifier) + + src_address = validate_identifier_number(ins_ast.operation.tex_src_address_identifier) + src_swizzle = validate_swizzle(ins_ast.operation.tex_src_swizzle_identifier) + + return Instruction( + tags = tags, + masks = masks, + opcode = opcode, + tex_id = tex_id, + src_addr = src_address, # texture coordinate address + src_swizzle = src_swizzle, # texture coordinate swizzle + dst_addr = dst_addr, # temp address + dst_swizzle = dst_swizzle, # texture value swizzle + ) diff --git a/regs/assembler/fs/validator.py b/regs/assembler/fs/validator.py index fdcd18e..992ab31 100644 --- a/regs/assembler/fs/validator.py +++ b/regs/assembler/fs/validator.py @@ -1,530 +1,11 @@ -from pprint import pprint -from dataclasses import dataclass -from enum import Enum, IntEnum, auto -from collections import OrderedDict - -from assembler.fs.parser import Mod -from assembler.fs.keywords import _keyword_to_string, KW -from assembler.error import print_error -from assembler.validator import ValidatorError - -class SrcAddrType(Enum): - temp = auto() - const = auto() - float = auto() - -@dataclass -class SrcAddr: - type: SrcAddrType - value: int - -class SrcMod(IntEnum): - nop = 0 - neg = 1 - abs = 2 - nab = 3 - -class SrcpOp(IntEnum): - neg2 = 0 - sub = 1 - add = 2 - neg = 3 - -@dataclass -class Addr: - src0: SrcAddr = None - src1: SrcAddr = None - src2: SrcAddr = None - srcp: SrcpOp = None - -@dataclass -class AddrRGBAlpha: - alpha: Addr - rgb: Addr - -class RGBMask(IntEnum): - NONE = 0 - R = 1 - G = 2 - RG = 3 - B = 4 - RB = 5 - GB = 6 - RGB = 7 - -class AlphaMask(IntEnum): - NONE = 0 - A = 1 - -class Unit(Enum): - alpha = auto() - rgb = auto() - -class RGBOp(IntEnum): - MAD = 0 - DP3 = 1 - DP4 = 2 - D2A = 3 - MIN = 4 - MAX = 5 - CND = 7 - CMP = 8 - FRC = 9 - SOP = 10 - MDH = 11 - MDV = 12 - -class AlphaOp(IntEnum): - MAD = 0 - DP = 1 - MIN = 2 - MAX = 3 - CND = 5 - CMP = 6 - FRC = 7 - EX2 = 8 - LN2 = 9 - RCP = 10 - RSQ = 11 - SIN = 12 - COS = 13 - MDH = 14 - MDV = 15 - -@dataclass -class RGBDest: - addrd: int - wmask: RGBMask - omask: RGBMask - -@dataclass -class AlphaDest: - addrd: int - wmask: AlphaMask - omask: AlphaMask - -class SwizzleSelSrc(IntEnum): - src0 = 0 - src1 = 1 - src2 = 2 - srcp = 3 - -class Swizzle(IntEnum): - r = 0 - g = 1 - b = 2 - a = 3 - zero = 4 - half = 5 - one = 6 - unused = 7 - -@dataclass -class SwizzleSel: - src: SwizzleSelSrc - swizzle: list[Swizzle] - mod: Mod - -@dataclass -class AlphaOperation: - dest: AlphaDest - opcode: AlphaOp - sels: list[SwizzleSel] - -@dataclass -class RGBOperation: - dest: RGBDest - opcode: RGBOp - sels: list[SwizzleSel] - -class InstructionType(IntEnum): - ALU = 0 - OUT = 1 - FC = 2 - TEX = 3 - -@dataclass -class Instruction: - type: InstructionType - tex_sem_wait: bool - nop: bool - addr: Addr - alpha_op: AlphaOperation - rgb_op: RGBOperation - -def validate_identifier_number(token): - try: - return int(token.lexeme, 10) - except ValueError: - raise ValidatorError("invalid number", token) - -def keywords_to_string(keywords): - return [_keyword_to_string[s].decode("utf-8") for s in keywords] - -def validate_instruction_let_expressions(let_expressions): - src_keywords = [KW.SRC0, KW.SRC1, KW.SRC2, KW.SRCP] - src_keyword_strs = keywords_to_string(src_keywords) - rgb_alpha_swizzles = [b"rgb", b"a"] - - addr_rgb_alpha = AddrRGBAlpha(Addr(), Addr()) - - def set_src_by_keyword(addr, keyword, value): - if keyword == KW.SRC0: - addr.src0 = value - elif keyword == KW.SRC1: - addr.src1 = value - elif keyword == KW.SRC2: - addr.src2 = value - elif keyword == KW.SRCP: - addr.srcp = value - else: - assert False, keyword - - def src_value(expr, src): - if src in {KW.SRC0, KW.SRC1, KW.SRC2}: - keyword_to_src_addr_type = OrderedDict([ - (KW.TEMP, SrcAddrType.temp), - (KW.CONST, SrcAddrType.const), - (KW.FLOAT, SrcAddrType.float), - ]) - src_addr_type_strs = keywords_to_string(keyword_to_src_addr_type.keys()) - type_kw = expr.addr_keyword.keyword - if type_kw not in keyword_to_src_addr_type: - raise ValidatorError(f"invalid src addr type, expected one of {src_addr_type_strs}", expr.addr_keyword) - - type = keyword_to_src_addr_type[type_kw] - value = validate_identifier_number(expr.addr_value_identifier) - if type is SrcAddrType.float: - if value >= 128: - raise ValidatorError(f"invalid float value", expr.addr_value_identifier) - elif type is SrcAddrType.temp: - if value >= 128: - raise ValidatorError(f"invalid temp value", expr.addr_value_identifier) - elif type is SrcAddrType.const: - if value >= 256: - raise ValidatorError(f"invalid const value", expr.addr_value_identifier) - else: - assert False, (id(type), id(SrcAddrType.float)) - - return SrcAddr( - type, - value, - ) - elif src == KW.SRCP: - keyword_to_srcp_op = OrderedDict([ - (KW.NEG2, SrcpOp.neg2), - (KW.SUB, SrcpOp.sub), - (KW.ADD, SrcpOp.add), - (KW.NEG, SrcpOp.neg), - ]) - srcp_op_strs = keywords_to_string(keyword_to_srcp_op.keys()) - op = expr.addr_keyword.keyword - if op not in keyword_to_srcp_op: - raise ValidatorError(f"invalid srcp op, expected one of {srcp_op_strs}", expr.addr_keyword) - return keyword_to_srcp_op[op] - else: - assert False, src - - sources = set() - - for expr in let_expressions: - src = expr.src_keyword.keyword - if src not in src_keywords: - raise ValidatorError(f"invalid src keyword, expected one of {src_keyword_strs}", expr.src_keyword) - - src_swizzle = expr.src_swizzle_identifier.lexeme.lower() - if src_swizzle not in rgb_alpha_swizzles: - raise ValidatorError(f"invalid src swizzle, expected one of {rgb_alpha_swizzles}", expr.src_swizzle_identifier) - - source = (_keyword_to_string[src].lower(), src_swizzle) - if source in sources: - raise ValidatorError(f"duplicate source/swizzle in let expressions", expr.src_swizzle_identifier) - sources.add(source) - - value = src_value(expr, src) - - if src_swizzle == b"a": - set_src_by_keyword(addr_rgb_alpha.alpha, src, value) - elif src_swizzle == b"rgb": - set_src_by_keyword(addr_rgb_alpha.rgb, src, value) - else: - assert False, src_swizzle - - return addr_rgb_alpha - -def prevalidate_mask(dest_addr_swizzle, valid_masks): - # we don't know yet whether this is an Alpha operation or an RGB operation - swizzle_str = dest_addr_swizzle.swizzle_identifier.lexeme - if swizzle_str.lower() not in valid_masks: - raise ValidatorError(f"invalid write mask, expected one of {valid_masks}", dest_addr_swizzle.swizzle_identifier) - - mask = swizzle_str.lower() - return mask - -rgb_op_kws = OrderedDict([ - (KW.MAD, RGBOp.MAD), - (KW.DP3, RGBOp.DP3), - (KW.DP4, RGBOp.DP4), - (KW.D2A, RGBOp.D2A), - (KW.MIN, RGBOp.MIN), - (KW.MAX, RGBOp.MAX), - (KW.CND, RGBOp.CND), - (KW.CMP, RGBOp.CMP), - (KW.FRC, RGBOp.FRC), - (KW.SOP, RGBOp.SOP), - (KW.MDH, RGBOp.MDH), - (KW.MDV, RGBOp.MDV) -]) -alpha_op_kws = OrderedDict([ - (KW.MAD, AlphaOp.MAD), - (KW.DP, AlphaOp.DP), - (KW.MIN, AlphaOp.MIN), - (KW.MAX, AlphaOp.MAX), - (KW.CND, AlphaOp.CND), - (KW.CMP, AlphaOp.CMP), - (KW.FRC, AlphaOp.FRC), - (KW.EX2, AlphaOp.EX2), - (KW.LN2, AlphaOp.LN2), - (KW.RCP, AlphaOp.RCP), - (KW.RSQ, AlphaOp.RSQ), - (KW.SIN, AlphaOp.SIN), - (KW.COS, AlphaOp.COS), - (KW.MDH, AlphaOp.MDH), - (KW.MDV, AlphaOp.MDV) -]) - -rgb_masks = OrderedDict([ - (b"none" , RGBMask.NONE), - (b"r" , RGBMask.R), - (b"g" , RGBMask.G), - (b"rg" , RGBMask.RG), - (b"b" , RGBMask.B), - (b"rb" , RGBMask.RB), - (b"gb" , RGBMask.GB), - (b"rgb" , RGBMask.RGB), -]) - -alpha_masks = OrderedDict([ - (b"none" , AlphaMask.NONE), - (b"a" , AlphaMask.A), -]) - -alpha_only_ops = set(alpha_op_kws.keys()) - set(rgb_op_kws.keys()) -rgb_only_ops = set(rgb_op_kws.keys()) - set(alpha_op_kws.keys()) -all_ops = set(rgb_op_kws.keys()) | set(alpha_op_kws.keys()) - -alpha_only_masks = set(alpha_masks.keys()) - set(rgb_masks.keys()) -rgb_only_masks = set(rgb_masks.keys()) - set(alpha_masks.keys()) -all_masks = set(rgb_masks.keys()) | set(alpha_masks.keys()) - -def infer_operation_units(operations): - if len(operations) > 2: - raise ValidatorError("too many operations in instruction", operations[-1].opcode_keyword) - - units = [None, None] - for i, operation in enumerate(operations): - opcode = operation.opcode_keyword.keyword - if opcode not in all_ops: - raise ValidatorError(f"invalid opcode keyword, expected one of {all_ops}", operation.opcode_keyword) - - if len(operation.dest_addr_swizzles) > 2: - raise ValidationError("too many destinations in instruction", operation.dest_addr_swizzles[-1]) - - masks = set(prevalidate_mask(dest_addr_swizzle, all_masks) for dest_addr_swizzle in operation.dest_addr_swizzles) - - def infer_opcode_unit(): - if opcode in alpha_only_ops: - return Unit.alpha - if opcode in rgb_only_ops: - return Unit.rgb - return None - - def infer_mask_unit(): - if any(mask in alpha_only_masks for mask in masks): - return Unit.alpha - if any(mask in rgb_only_masks for mask in masks): - return Unit.rgb - return None - - opcode_unit = infer_opcode_unit() - mask_unit = infer_mask_unit() - if opcode_unit is not None and mask_unit is not None and opcode_unit != mask_unit: - raise ValidatorError(f"contradictory {mask_unit.name} write mask for {opcode_unit.name} opcode", operation.opcode_keyword) - units[i] = opcode_unit or mask_unit - - if units[0] == units[1]: - raise ValidatorError(f"invalid duplicate use of {units[1].name} unit", operations[1].opcode_keyword) - - other_unit = { - Unit.alpha: Unit.rgb, - Unit.rgb: Unit.alpha, - } - - if units[0] is None: - units[0] = other_unit[units[1]] - if units[1] is None: - units[1] = other_unit[units[0]] - - assert units[0] is not None - assert units[1] is not None - assert units[0] != units[1] - - for i, operation in enumerate(operations): - yield units[i], operation - -def validate_dest_keyword(dest_keyword): - dest_keywords = [KW.OUT, KW.TEMP] - dest_keyword_strs = keywords_to_string(dest_keywords) - dest = dest_keyword.keyword - if dest not in dest_keywords: - raise ValidatorError(f"invalid dest keyword, expected one of {dest_keyword_strs}", dest_addr_swizzle.dest_keyword) - return dest - -def validate_instruction_operation_dest(dest_addr_swizzles, mask_lookup, type_cls): - addrs = set() - wmask = None - omask = None - for dest_addr_swizzle in dest_addr_swizzles: - dest = validate_dest_keyword(dest_addr_swizzle.dest_keyword) - addr = validate_identifier_number(dest_addr_swizzle.addr_identifier) - mask = mask_lookup[dest_addr_swizzle.swizzle_identifier.lexeme.lower()] - addrs.add(addr) - if dest == KW.OUT: - omask = mask - elif dest == KW.TEMP: - wmask = mask - else: - assert False, dest - if len(addrs) > 1: - raise ValidatorError(f"too many destination addresses", operation.dest_addr_swizzles[-1].addr_identifier) - addrd, = addrs if addrs else [0] - return type_cls( - addrd=addrd, - wmask=wmask, - omask=omask - ) - -swizzle_sel_src_kws = OrderedDict([ - (KW.SRC0, SwizzleSelSrc.src0), - (KW.SRC1, SwizzleSelSrc.src1), - (KW.SRC2, SwizzleSelSrc.src2), - (KW.SRCP, SwizzleSelSrc.srcp), -]) - -swizzle_kws = OrderedDict([ - (ord("r"), Swizzle.r), - (ord("g"), Swizzle.g), - (ord("b"), Swizzle.b), - (ord("a"), Swizzle.a), - (ord("0"), Swizzle.zero), - (ord("h"), Swizzle.half), - (ord("1"), Swizzle.one), - (ord("_"), Swizzle.unused), -]) - -def validate_instruction_operation_sels(swizzle_sels, is_alpha): - if len(swizzle_sels) > 3: - raise ValidatorError("too many swizzle sels", swizzle_sels[-1].sel_keyword) - - sels = [] - for swizzle_sel in swizzle_sels: - if swizzle_sel.sel_keyword.keyword not in swizzle_sel_src_kws: - raise ValidatorError("invalid swizzle src", swizzle_sel.sel_keyword.keyword) - src = swizzle_sel_src_kws[swizzle_sel.sel_keyword.keyword] - - swizzle_lexeme = swizzle_sel.swizzle_identifier.lexeme.lower() - swizzles_length = 1 if is_alpha else 3 - if len(swizzle_lexeme) != swizzles_length: - raise ValidatorError("invalid swizzle length", swizzle_sel.swizzle_identifier) - if not all(c in swizzle_kws for c in swizzle_lexeme): - raise ValidatorError("invalid swizzle characters", swizzle_sel.swizzle_identifier) - swizzle = [ - swizzle_kws[c] for c in swizzle_lexeme - ] - - mod = swizzle_sel.mod - sels.append(SwizzleSel(src, swizzle, mod)) - return sels - -def validate_alpha_instruction_operation(operation): - dest = validate_instruction_operation_dest(operation.dest_addr_swizzles, - mask_lookup=alpha_masks, - type_cls=AlphaDest) - opcode = alpha_op_kws[operation.opcode_keyword.keyword] - sels = validate_instruction_operation_sels(operation.swizzle_sels, is_alpha=True) - return AlphaOperation( - dest, - opcode, - sels - ) - -def validate_rgb_instruction_operation(operation): - dest = validate_instruction_operation_dest(operation.dest_addr_swizzles, - mask_lookup=rgb_masks, - type_cls=RGBDest) - opcode = rgb_op_kws[operation.opcode_keyword.keyword] - sels = validate_instruction_operation_sels(operation.swizzle_sels, is_alpha=False) - return RGBOperation( - dest, - opcode, - sels - ) - -def validate_instruction_operations(operations): - for unit, operation in infer_operation_units(operations): - if unit is Unit.alpha: - yield validate_alpha_instruction_operation(operation) - elif unit is Unit.rgb: - yield validate_rgb_instruction_operation(operation) - else: - assert False, unit +from assembler.fs import alu_validator +from assembler.fs import tex_validator +from assembler.fs.parser import TEXInstruction, ALUInstruction def validate_instruction(ins): - addr_rgb_alpha = validate_instruction_let_expressions(ins.let_expressions) - - instruction_type = InstructionType.OUT if ins.out else InstructionType.ALU - tex_sem_wait = ins.tex_sem_wait - nop = ins.nop - - instruction = Instruction( - instruction_type, - tex_sem_wait, - nop, - addr_rgb_alpha, - None, - None - ) - for op in validate_instruction_operations(ins.operations): - if type(op) is RGBOperation: - instruction.rgb_op = op - elif type(op) is AlphaOperation: - instruction.alpha_op = op - else: - assert False, op - return instruction - -if __name__ == "__main__": - from assembler.lexer import Lexer, LexerError - from assembler.fs.parser import Parser, ParserError - from assembler.fs.keywords import find_keyword - - buf = b""" -src0.a = float(0), src0.rgb = temp[0] , srcp.a = neg : - out[0].none = temp[0].none = MAD src0.r src0.r src0.r , - out[0].none = temp[0].r = DP3 src0.rg0 src0.rg0 ; - """ - lexer = Lexer(buf, find_keyword, emit_newlines=False, minus_is_token=True) - tokens = list(lexer.lex_tokens()) - parser = Parser(tokens) - try: - ins_ast = parser.instruction() - pprint(validate_instruction(ins_ast)) - except LexerError as e: - print_error(None, buf, e) - raise - except ParserError as e: - print_error(None, buf, e) - raise - except ValidatorError as e: - print_error(None, buf, e) - raise + if type(ins) is TEXInstruction: + return tex_validator.validate_instruction(ins) + elif type(ins) is ALUInstruction: + return alu_validator.validate_instruction(ins) + else: + assert False, type(ins) diff --git a/regs/assembler/parser.py b/regs/assembler/parser.py index fd633bf..8c076f4 100644 --- a/regs/assembler/parser.py +++ b/regs/assembler/parser.py @@ -44,3 +44,11 @@ class BaseParser: if token.type != token_type1 and token.type != token_type2: raise ParserError(message, token) return token + + def consume_keyword(self, keyword, message): + if self.match_keyword(keyword): + token = self.advance() + return token + else: + token = self.advance() + raise ParserError(message, token) diff --git a/regs/us_disassemble.py b/regs/us_disassemble.py index baddca1..fbd15b9 100644 --- a/regs/us_disassemble.py +++ b/regs/us_disassemble.py @@ -130,8 +130,8 @@ def disassemble(code, ix): for i, register_name in enumerate(register_name_list): print('\n'.join(inner2(i, register_name))) - #inner = inner_rows - inner = inner_columns + inner = inner_rows + #inner = inner_columns inst_type = get_field_pv_name(us_cmn_inst, US_CMN_INST["TYPE"]) if inst_type in {"US_INST_TYPE_OUT", "US_INST_TYPE_ALU"}: diff --git a/regs/us_disassemble2.py b/regs/us_disassemble2.py index dc679ee..937a2df 100644 --- a/regs/us_disassemble2.py +++ b/regs/us_disassemble2.py @@ -284,7 +284,7 @@ def assert_zeros_tex(code): src_addr_rel = US_TEX_ADDR.SRC_ADDR_REL(code) assert src_addr_rel == 0, src_addr_rel - dst_addr_rel = US_TEX_ADDR.SRC_ADDR_REL(code) + dst_addr_rel = US_TEX_ADDR.DST_ADDR_REL(code) assert dst_addr_rel == 0, dst_addr_rel ignore_uncovered = US_TEX_INST.IGNORE_UNCOVERED(code) @@ -445,9 +445,11 @@ def disassemble_tex(code): tags.append("TEX_SEM_WAIT") if US_TEX_INST.TEX_SEM_ACQUIRE(code): tags.append("TEX_SEM_ACQUIRE") + if US_CMN_INST.ALU_WAIT(code): + tags.append("ALU_WAIT") print(" ".join(tags)) - print(f" {temp_out_str}{inst} tex[{tex_id}].{dst_swiz} temp[{tex_id}].{src_swiz} ;") + print(f" {temp_out_str}{inst} tex[{tex_id}].{dst_swiz} temp[{src_addr}].{src_swiz} ;") def disassemble(code): assert len(code) == 6, len(code) diff --git a/shader_examples/mesa/texture_dual.fs.txt b/shader_examples/mesa/texture_dual.fs.txt index 4064c21..96472f3 100644 --- a/shader_examples/mesa/texture_dual.fs.txt +++ b/shader_examples/mesa/texture_dual.fs.txt @@ -4,12 +4,14 @@ 0x00000000, 0x00000000, 0x00000000, + 0x00007807, 0x02410000, 0xe402f400, 0x00000000, 0x00000000, 0x00000000, + 0x00078005, 0x40000801, 0x48000801,