assembler.fs: add support for TEX instructions

This commit is contained in:
Zack Buhman 2025-10-25 19:41:19 -05:00
parent 27227426ea
commit 96d7286e7c
15 changed files with 1097 additions and 760 deletions

View File

@ -20,11 +20,11 @@
0x00000000,
0x00004000,
0x08020080,
0x10320080,
0x0802f400,
0x00000000,
0x0068c000,
0x20000000,
0x04000000,
0x00004000,
0x08020080,

View 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)

View 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

View 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")

View 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]

View File

@ -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)

View File

@ -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()}

View File

@ -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):
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()
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
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()

View 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)

View 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
)

View File

@ -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
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, 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
assert False, type(ins)

View File

@ -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)

View File

@ -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"}:

View File

@ -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)

View File

@ -4,12 +4,14 @@
0x00000000,
0x00000000,
0x00000000,
0x00007807,
0x02410000,
0xe402f400,
0x00000000,
0x00000000,
0x00000000,
0x00078005,
0x40000801,
0x48000801,