assembler.fs: add support for TEX instructions
This commit is contained in:
parent
27227426ea
commit
96d7286e7c
@ -20,11 +20,11 @@
|
|||||||
0x00000000,
|
0x00000000,
|
||||||
|
|
||||||
0x00004000,
|
0x00004000,
|
||||||
0x08020080,
|
0x10320080,
|
||||||
0x0802f400,
|
0x0802f400,
|
||||||
0x00000000,
|
0x00000000,
|
||||||
0x0068c000,
|
0x0068c000,
|
||||||
0x20000000,
|
0x04000000,
|
||||||
|
|
||||||
0x00004000,
|
0x00004000,
|
||||||
0x08020080,
|
0x08020080,
|
||||||
|
|||||||
115
regs/assembler/fs/alu_emitter.py
Normal file
115
regs/assembler/fs/alu_emitter.py
Normal file
@ -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)
|
||||||
490
regs/assembler/fs/alu_validator.py
Normal file
490
regs/assembler/fs/alu_validator.py
Normal file
@ -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
|
||||||
69
regs/assembler/fs/common_emitter.py
Normal file
69
regs/assembler/fs/common_emitter.py
Normal file
@ -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")
|
||||||
43
regs/assembler/fs/common_validator.py
Normal file
43
regs/assembler/fs/common_validator.py
Normal file
@ -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]
|
||||||
@ -1,180 +1,12 @@
|
|||||||
from os import path
|
from assembler.fs import alu_emitter
|
||||||
from pprint import pprint
|
from assembler.fs import tex_emitter
|
||||||
from functools import partial
|
from assembler.fs import alu_validator
|
||||||
|
from assembler.fs import tex_validator
|
||||||
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)
|
|
||||||
|
|
||||||
def emit_instruction(code, ins):
|
def emit_instruction(code, ins):
|
||||||
US_CMN_INST.TYPE(code, ins.type.value)
|
if type(ins) is alu_validator.Instruction:
|
||||||
US_CMN_INST.TEX_SEM_WAIT(code, int(ins.tex_sem_wait))
|
return alu_emitter.emit_instruction(code, ins)
|
||||||
US_CMN_INST.NOP(code, int(ins.nop))
|
elif type(ins) is tex_validator.Instruction:
|
||||||
|
return tex_emitter.emit_instruction(code, ins)
|
||||||
emit_addr(code, ins.addr)
|
else:
|
||||||
if ins.alpha_op is not None:
|
assert False, type(ins)
|
||||||
emit_alpha_op(code, ins.alpha_op)
|
|
||||||
if ins.rgb_op is not None:
|
|
||||||
emit_rgb_op(code, ins.rgb_op)
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class KW(Enum):
|
|||||||
# modifiers
|
# modifiers
|
||||||
NOP = auto()
|
NOP = auto()
|
||||||
TEX_SEM_WAIT = 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 = {
|
_string_to_keyword = {
|
||||||
b"CMP": KW.CMP,
|
b"CMP": KW.CMP,
|
||||||
@ -76,6 +88,16 @@ _string_to_keyword = {
|
|||||||
b"NEG": KW.NEG,
|
b"NEG": KW.NEG,
|
||||||
b"NOP": KW.NOP,
|
b"NOP": KW.NOP,
|
||||||
b"TEX_SEM_WAIT": KW.TEX_SEM_WAIT,
|
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()}
|
_keyword_to_string = {v:k for k,v in _string_to_keyword.items()}
|
||||||
|
|
||||||
|
|||||||
@ -7,14 +7,14 @@ from assembler.lexer import TT, Token
|
|||||||
from assembler.fs.keywords import KW, find_keyword
|
from assembler.fs.keywords import KW, find_keyword
|
||||||
from assembler.error import print_error
|
from assembler.error import print_error
|
||||||
|
|
||||||
class Mod(IntEnum):
|
class ALUMod(IntEnum):
|
||||||
nop = 0
|
nop = 0
|
||||||
neg = 1
|
neg = 1
|
||||||
abs = 2
|
abs = 2
|
||||||
nab = 3
|
nab = 3
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LetExpression:
|
class ALULetExpression:
|
||||||
src_keyword: Token
|
src_keyword: Token
|
||||||
src_swizzle_identifier: Token
|
src_swizzle_identifier: Token
|
||||||
addr_keyword: Token
|
addr_keyword: Token
|
||||||
@ -27,27 +27,39 @@ class DestAddrSwizzle:
|
|||||||
swizzle_identifier: Token
|
swizzle_identifier: Token
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SwizzleSel:
|
class ALUSwizzleSel:
|
||||||
sel_keyword: Token
|
sel_keyword: Token
|
||||||
swizzle_identifier: Token
|
swizzle_identifier: Token
|
||||||
mod: Mod
|
mod: ALUMod
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Operation:
|
class ALUOperation:
|
||||||
dest_addr_swizzles: list[DestAddrSwizzle]
|
dest_addr_swizzles: list[DestAddrSwizzle]
|
||||||
opcode_keyword: Token
|
opcode_keyword: Token
|
||||||
swizzle_sels: list[SwizzleSel]
|
swizzle_sels: list[ALUSwizzleSel]
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Instruction:
|
class ALUInstruction:
|
||||||
out: bool
|
tags: set[Token]
|
||||||
tex_sem_wait: bool
|
let_expressions: list[ALULetExpression]
|
||||||
nop: bool
|
operations: list[ALUOperation]
|
||||||
let_expressions: list[LetExpression]
|
|
||||||
operations: list[Operation]
|
@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):
|
class Parser(BaseParser):
|
||||||
def let_expression(self):
|
def alu_let_expression(self):
|
||||||
src_keyword = self.consume(TT.keyword, "expected src keyword")
|
src_keyword = self.consume(TT.keyword, "expected src keyword")
|
||||||
self.consume(TT.dot, "expected dot")
|
self.consume(TT.dot, "expected dot")
|
||||||
src_swizzle_identifier = self.consume(TT.identifier, "expected src swizzle identifier")
|
src_swizzle_identifier = self.consume(TT.identifier, "expected src swizzle identifier")
|
||||||
@ -69,7 +81,7 @@ class Parser(BaseParser):
|
|||||||
else:
|
else:
|
||||||
self.consume(TT.right_square, "expected right square")
|
self.consume(TT.right_square, "expected right square")
|
||||||
|
|
||||||
return LetExpression(
|
return ALULetExpression(
|
||||||
src_keyword,
|
src_keyword,
|
||||||
src_swizzle_identifier,
|
src_swizzle_identifier,
|
||||||
addr_keyword,
|
addr_keyword,
|
||||||
@ -90,7 +102,7 @@ class Parser(BaseParser):
|
|||||||
swizzle_identifier,
|
swizzle_identifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_opcode(self):
|
def alu_is_opcode(self):
|
||||||
opcode_keywords = {
|
opcode_keywords = {
|
||||||
KW.CMP, KW.CND, KW.COS, KW.D2A,
|
KW.CMP, KW.CND, KW.COS, KW.D2A,
|
||||||
KW.DP , KW.DP3, KW.DP4, KW.EX2,
|
KW.DP , KW.DP3, KW.DP4, KW.EX2,
|
||||||
@ -103,21 +115,21 @@ class Parser(BaseParser):
|
|||||||
return token.keyword in opcode_keywords
|
return token.keyword in opcode_keywords
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_neg(self):
|
def alu_is_neg(self):
|
||||||
result = self.match(TT.minus)
|
result = self.match(TT.minus)
|
||||||
if result:
|
if result:
|
||||||
self.advance()
|
self.advance()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def is_abs(self):
|
def alu_is_abs(self):
|
||||||
result = self.match(TT.bar)
|
result = self.match(TT.bar)
|
||||||
if result:
|
if result:
|
||||||
self.advance()
|
self.advance()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def swizzle_sel(self):
|
def alu_swizzle_sel(self):
|
||||||
neg = self.is_neg()
|
neg = self.alu_is_neg()
|
||||||
abs = self.is_abs()
|
abs = self.alu_is_abs()
|
||||||
|
|
||||||
sel_keyword = self.consume(TT.keyword, "expected sel keyword")
|
sel_keyword = self.consume(TT.keyword, "expected sel keyword")
|
||||||
self.consume(TT.dot, "expected dot")
|
self.consume(TT.dot, "expected dot")
|
||||||
@ -128,52 +140,51 @@ class Parser(BaseParser):
|
|||||||
|
|
||||||
mod_table = {
|
mod_table = {
|
||||||
# (neg, abs)
|
# (neg, abs)
|
||||||
(False, False): Mod.nop,
|
(False, False): ALUMod.nop,
|
||||||
(False, True): Mod.abs,
|
(False, True): ALUMod.abs,
|
||||||
(True, False): Mod.neg,
|
(True, False): ALUMod.neg,
|
||||||
(True, True): Mod.nab,
|
(True, True): ALUMod.nab,
|
||||||
}
|
}
|
||||||
mod = mod_table[(neg, abs)]
|
mod = mod_table[(neg, abs)]
|
||||||
return SwizzleSel(
|
return ALUSwizzleSel(
|
||||||
sel_keyword,
|
sel_keyword,
|
||||||
swizzle_identifier,
|
swizzle_identifier,
|
||||||
mod,
|
mod,
|
||||||
)
|
)
|
||||||
|
|
||||||
def operation(self):
|
def alu_operation(self):
|
||||||
dest_addr_swizzles = []
|
dest_addr_swizzles = []
|
||||||
while not self.is_opcode():
|
while not self.alu_is_opcode():
|
||||||
dest_addr_swizzles.append(self.dest_addr_swizzle())
|
dest_addr_swizzles.append(self.dest_addr_swizzle())
|
||||||
|
|
||||||
opcode_keyword = self.consume(TT.keyword, "expected opcode keyword")
|
opcode_keyword = self.consume(TT.keyword, "expected opcode keyword")
|
||||||
|
|
||||||
swizzle_sels = []
|
swizzle_sels = []
|
||||||
while not (self.match(TT.comma) or self.match(TT.semicolon)):
|
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,
|
dest_addr_swizzles,
|
||||||
opcode_keyword,
|
opcode_keyword,
|
||||||
swizzle_sels
|
swizzle_sels
|
||||||
)
|
)
|
||||||
|
|
||||||
def instruction(self):
|
def alu_instruction(self, out: bool):
|
||||||
out = False
|
tags = list()
|
||||||
if self.match_keyword(KW.OUT):
|
tag_keywords = set([KW.OUT, KW.TEX_SEM_WAIT, KW.NOP, KW.LAST, KW.ALU_WAIT])
|
||||||
self.advance()
|
while True:
|
||||||
out = True
|
if self.match(TT.keyword):
|
||||||
tex_sem_wait = False
|
token = self.peek()
|
||||||
if self.match_keyword(KW.TEX_SEM_WAIT):
|
if token.keyword in tag_keywords:
|
||||||
self.advance()
|
self.advance()
|
||||||
tex_sem_wait = True
|
tag_keywords.remove(token.keyword)
|
||||||
nop = False
|
tags.append(token)
|
||||||
if self.match_keyword(KW.NOP):
|
continue
|
||||||
self.advance()
|
break
|
||||||
nop = True
|
|
||||||
|
|
||||||
let_expressions = []
|
let_expressions = []
|
||||||
while not self.match(TT.colon):
|
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):
|
if not self.match(TT.colon):
|
||||||
self.consume(TT.comma, "expected comma")
|
self.consume(TT.comma, "expected comma")
|
||||||
|
|
||||||
@ -181,20 +192,87 @@ class Parser(BaseParser):
|
|||||||
|
|
||||||
operations = []
|
operations = []
|
||||||
while not self.match(TT.semicolon):
|
while not self.match(TT.semicolon):
|
||||||
operations.append(self.operation())
|
operations.append(self.alu_operation())
|
||||||
if not self.match(TT.semicolon):
|
if not self.match(TT.semicolon):
|
||||||
self.consume(TT.comma, "expected comma")
|
self.consume(TT.comma, "expected comma")
|
||||||
|
|
||||||
self.consume(TT.semicolon, "expected semicolon")
|
self.consume(TT.semicolon, "expected semicolon")
|
||||||
|
|
||||||
return Instruction(
|
return ALUInstruction(
|
||||||
out,
|
tags,
|
||||||
tex_sem_wait,
|
|
||||||
nop,
|
|
||||||
let_expressions,
|
let_expressions,
|
||||||
operations,
|
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):
|
def instructions(self):
|
||||||
while not self.match(TT.eof):
|
while not self.match(TT.eof):
|
||||||
yield self.instruction()
|
yield self.instruction()
|
||||||
|
|||||||
36
regs/assembler/fs/tex_emitter.py
Normal file
36
regs/assembler/fs/tex_emitter.py
Normal file
@ -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)
|
||||||
159
regs/assembler/fs/tex_validator.py
Normal file
159
regs/assembler/fs/tex_validator.py
Normal file
@ -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
|
||||||
|
)
|
||||||
@ -1,530 +1,11 @@
|
|||||||
from pprint import pprint
|
from assembler.fs import alu_validator
|
||||||
from dataclasses import dataclass
|
from assembler.fs import tex_validator
|
||||||
from enum import Enum, IntEnum, auto
|
from assembler.fs.parser import TEXInstruction, ALUInstruction
|
||||||
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
|
|
||||||
|
|
||||||
def validate_instruction(ins):
|
def validate_instruction(ins):
|
||||||
addr_rgb_alpha = validate_instruction_let_expressions(ins.let_expressions)
|
if type(ins) is TEXInstruction:
|
||||||
|
return tex_validator.validate_instruction(ins)
|
||||||
instruction_type = InstructionType.OUT if ins.out else InstructionType.ALU
|
elif type(ins) is ALUInstruction:
|
||||||
tex_sem_wait = ins.tex_sem_wait
|
return alu_validator.validate_instruction(ins)
|
||||||
nop = ins.nop
|
else:
|
||||||
|
assert False, type(ins)
|
||||||
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
|
|
||||||
|
|||||||
@ -44,3 +44,11 @@ class BaseParser:
|
|||||||
if token.type != token_type1 and token.type != token_type2:
|
if token.type != token_type1 and token.type != token_type2:
|
||||||
raise ParserError(message, token)
|
raise ParserError(message, token)
|
||||||
return 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)
|
||||||
|
|||||||
@ -130,8 +130,8 @@ def disassemble(code, ix):
|
|||||||
for i, register_name in enumerate(register_name_list):
|
for i, register_name in enumerate(register_name_list):
|
||||||
print('\n'.join(inner2(i, register_name)))
|
print('\n'.join(inner2(i, register_name)))
|
||||||
|
|
||||||
#inner = inner_rows
|
inner = inner_rows
|
||||||
inner = inner_columns
|
#inner = inner_columns
|
||||||
|
|
||||||
inst_type = get_field_pv_name(us_cmn_inst, US_CMN_INST["TYPE"])
|
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"}:
|
if inst_type in {"US_INST_TYPE_OUT", "US_INST_TYPE_ALU"}:
|
||||||
|
|||||||
@ -284,7 +284,7 @@ def assert_zeros_tex(code):
|
|||||||
src_addr_rel = US_TEX_ADDR.SRC_ADDR_REL(code)
|
src_addr_rel = US_TEX_ADDR.SRC_ADDR_REL(code)
|
||||||
assert src_addr_rel == 0, src_addr_rel
|
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
|
assert dst_addr_rel == 0, dst_addr_rel
|
||||||
|
|
||||||
ignore_uncovered = US_TEX_INST.IGNORE_UNCOVERED(code)
|
ignore_uncovered = US_TEX_INST.IGNORE_UNCOVERED(code)
|
||||||
@ -445,9 +445,11 @@ def disassemble_tex(code):
|
|||||||
tags.append("TEX_SEM_WAIT")
|
tags.append("TEX_SEM_WAIT")
|
||||||
if US_TEX_INST.TEX_SEM_ACQUIRE(code):
|
if US_TEX_INST.TEX_SEM_ACQUIRE(code):
|
||||||
tags.append("TEX_SEM_ACQUIRE")
|
tags.append("TEX_SEM_ACQUIRE")
|
||||||
|
if US_CMN_INST.ALU_WAIT(code):
|
||||||
|
tags.append("ALU_WAIT")
|
||||||
|
|
||||||
print(" ".join(tags))
|
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):
|
def disassemble(code):
|
||||||
assert len(code) == 6, len(code)
|
assert len(code) == 6, len(code)
|
||||||
|
|||||||
@ -4,12 +4,14 @@
|
|||||||
0x00000000,
|
0x00000000,
|
||||||
0x00000000,
|
0x00000000,
|
||||||
0x00000000,
|
0x00000000,
|
||||||
|
|
||||||
0x00007807,
|
0x00007807,
|
||||||
0x02410000,
|
0x02410000,
|
||||||
0xe402f400,
|
0xe402f400,
|
||||||
0x00000000,
|
0x00000000,
|
||||||
0x00000000,
|
0x00000000,
|
||||||
0x00000000,
|
0x00000000,
|
||||||
|
|
||||||
0x00078005,
|
0x00078005,
|
||||||
0x40000801,
|
0x40000801,
|
||||||
0x48000801,
|
0x48000801,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user