use crate::{Stage, Version};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Opcode {
Nop,
Mov,
Add,
Sub,
Mad,
Mul,
Rcp,
Rsq,
Dp3,
Dp4,
Min,
Max,
Slt,
Sge,
Exp,
Log,
Lit,
Dst,
Lrp,
Frc,
M4x4,
M4x3,
M3x4,
M3x3,
M3x2,
Call,
CallNz,
Loop,
Ret,
EndLoop,
Label,
Dcl,
Pow,
Crs,
Sgn,
Abs,
Nrm,
SinCos,
Rep,
EndRep,
If,
IfC,
Else,
EndIf,
Break,
BreakC,
Mova,
DefB,
DefI,
TexCoord,
TexKill,
Tex,
TexBem,
TexBemL,
TexReg2Ar,
TexReg2Gb,
TexM3x2Pad,
TexM3x2Tex,
TexM3x3Pad,
TexM3x3Tex,
TexM3x3Spec,
TexM3x3VSpec,
ExpP,
LogP,
Cnd,
Def,
TexReg2Rgb,
TexDp3Tex,
TexM3x2Depth,
TexDp3,
TexM3x3,
TexDepth,
Cmp,
Bem,
Dp2Add,
Dsx,
Dsy,
TexLdd,
SetP,
TexLdl,
BreakP,
Phase,
}
const TABLE: &[(Opcode, u16, &str)] = &[
(Opcode::Nop, 0, "nop"),
(Opcode::Mov, 1, "mov"),
(Opcode::Add, 2, "add"),
(Opcode::Sub, 3, "sub"),
(Opcode::Mad, 4, "mad"),
(Opcode::Mul, 5, "mul"),
(Opcode::Rcp, 6, "rcp"),
(Opcode::Rsq, 7, "rsq"),
(Opcode::Dp3, 8, "dp3"),
(Opcode::Dp4, 9, "dp4"),
(Opcode::Min, 10, "min"),
(Opcode::Max, 11, "max"),
(Opcode::Slt, 12, "slt"),
(Opcode::Sge, 13, "sge"),
(Opcode::Exp, 14, "exp"),
(Opcode::Log, 15, "log"),
(Opcode::Lit, 16, "lit"),
(Opcode::Dst, 17, "dst"),
(Opcode::Lrp, 18, "lrp"),
(Opcode::Frc, 19, "frc"),
(Opcode::M4x4, 20, "m4x4"),
(Opcode::M4x3, 21, "m4x3"),
(Opcode::M3x4, 22, "m3x4"),
(Opcode::M3x3, 23, "m3x3"),
(Opcode::M3x2, 24, "m3x2"),
(Opcode::Call, 25, "call"),
(Opcode::CallNz, 26, "callnz"),
(Opcode::Loop, 27, "loop"),
(Opcode::Ret, 28, "ret"),
(Opcode::EndLoop, 29, "endloop"),
(Opcode::Label, 30, "label"),
(Opcode::Dcl, 31, "dcl"),
(Opcode::Pow, 32, "pow"),
(Opcode::Crs, 33, "crs"),
(Opcode::Sgn, 34, "sgn"),
(Opcode::Abs, 35, "abs"),
(Opcode::Nrm, 36, "nrm"),
(Opcode::SinCos, 37, "sincos"),
(Opcode::Rep, 38, "rep"),
(Opcode::EndRep, 39, "endrep"),
(Opcode::If, 40, "if"),
(Opcode::IfC, 41, "if_comp"),
(Opcode::Else, 42, "else"),
(Opcode::EndIf, 43, "endif"),
(Opcode::Break, 44, "break"),
(Opcode::BreakC, 45, "break_comp"),
(Opcode::Mova, 46, "mova"),
(Opcode::DefB, 47, "defb"),
(Opcode::DefI, 48, "defi"),
(Opcode::TexCoord, 64, "texcoord"),
(Opcode::TexKill, 65, "texkill"),
(Opcode::Tex, 66, "tex"),
(Opcode::TexBem, 67, "texbem"),
(Opcode::TexBemL, 68, "texbeml"),
(Opcode::TexReg2Ar, 69, "texreg2ar"),
(Opcode::TexReg2Gb, 70, "texreg2gb"),
(Opcode::TexM3x2Pad, 71, "texm3x2pad"),
(Opcode::TexM3x2Tex, 72, "texm3x2tex"),
(Opcode::TexM3x3Pad, 73, "texm3x3pad"),
(Opcode::TexM3x3Tex, 74, "texm3x3tex"),
(Opcode::TexM3x3Spec, 76, "texm3x3spec"),
(Opcode::TexM3x3VSpec, 77, "texm3x3vspec"),
(Opcode::ExpP, 78, "expp"),
(Opcode::LogP, 79, "logp"),
(Opcode::Cnd, 80, "cnd"),
(Opcode::Def, 81, "def"),
(Opcode::TexReg2Rgb, 82, "texreg2rgb"),
(Opcode::TexDp3Tex, 83, "texdp3tex"),
(Opcode::TexM3x2Depth, 84, "texm3x2depth"),
(Opcode::TexDp3, 85, "texdp3"),
(Opcode::TexM3x3, 86, "texm3x3"),
(Opcode::TexDepth, 87, "texdepth"),
(Opcode::Cmp, 88, "cmp"),
(Opcode::Bem, 89, "bem"),
(Opcode::Dp2Add, 90, "dp2add"),
(Opcode::Dsx, 91, "dsx"),
(Opcode::Dsy, 92, "dsy"),
(Opcode::TexLdd, 93, "texldd"),
(Opcode::SetP, 94, "setp_comp"),
(Opcode::TexLdl, 95, "texldl"),
(Opcode::BreakP, 96, "breakp"),
(Opcode::Phase, 0xfffd, "phase"),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Shape {
Operands { destination: bool, sources: usize },
Declaration,
Definition { words: usize },
Phase,
}
type Range = Option<((u8, u8), (u8, u8))>;
const ALL: Range = Some(((1, 0), (3, 255)));
const FROM_2: Range = Some(((2, 0), (3, 255)));
const FROM_2X: Range = Some(((2, 1), (3, 255)));
const FROM_3: Range = Some(((3, 0), (3, 255)));
const NONE: Range = None;
const fn pixel(from: (u8, u8), to: (u8, u8)) -> Range {
Some((from, to))
}
impl Opcode {
pub fn code(self) -> u16 {
TABLE
.iter()
.find(|row| row.0 == self)
.map_or(u16::MAX, |row| row.1)
}
pub fn name(self) -> &'static str {
TABLE.iter().find(|row| row.0 == self).map_or("", |row| row.2)
}
pub fn from_code(code: u16) -> Option<Self> {
TABLE.iter().find(|row| row.1 == code).map(|row| row.0)
}
fn versions(self) -> (Range, Range) {
use Opcode::*;
match self {
Nop | Mov | Add | Sub | Mad | Mul | Dp3 | Def => (ALL, ALL),
Dcl => (ALL, FROM_2),
Rcp | Rsq | Min | Max | Exp | Log | Frc | M4x4 | M4x3 | M3x4 | M3x3 | M3x2 => (ALL, FROM_2),
Dp4 => (ALL, pixel((1, 2), (3, 255))),
Slt | Sge | Lit | Dst | ExpP | LogP => (ALL, NONE),
Lrp => (FROM_2, ALL),
Pow | Crs | Abs | Nrm | SinCos => (FROM_2, FROM_2),
Sgn | Mova => (FROM_2, NONE),
Call | CallNz | Ret | Label | Rep | EndRep | If | Else | EndIf | DefB | DefI => (FROM_2, FROM_2X),
Loop | EndLoop => (FROM_2, FROM_3),
IfC | Break | BreakC | SetP | BreakP => (FROM_2X, FROM_2X),
TexLdl => (FROM_3, FROM_3),
TexKill | Tex => (NONE, ALL),
TexCoord => (NONE, pixel((1, 0), (1, 4))),
TexBem | TexBemL | TexReg2Ar | TexReg2Gb | TexM3x2Pad | TexM3x2Tex | TexM3x3Pad | TexM3x3Tex
| TexM3x3Spec | TexM3x3VSpec => (NONE, pixel((1, 0), (1, 3))),
TexReg2Rgb | TexDp3Tex | TexDp3 | TexM3x3 => (NONE, pixel((1, 2), (1, 3))),
TexM3x2Depth => (NONE, pixel((1, 3), (1, 3))),
Cnd => (NONE, pixel((1, 0), (1, 4))),
Cmp => (NONE, pixel((1, 2), (3, 255))),
TexDepth | Bem | Phase => (NONE, pixel((1, 4), (1, 4))),
Dp2Add => (NONE, FROM_2),
Dsx | Dsy | TexLdd => (NONE, FROM_2X),
}
}
pub fn in_version(self, version: Version) -> bool {
let (vertex, pixel) = self.versions();
let range = match version.stage {
Stage::Vertex => vertex,
Stage::Pixel => pixel,
};
range.is_some_and(|(from, to)| (from..=to).contains(&(version.major, version.minor)))
}
pub(crate) fn shape(self, version: Version) -> Shape {
use Opcode::*;
let sm1 = version.major == 1;
let sm3 = version.major == 3;
let (destination, sources) = match self {
Dcl => return Shape::Declaration,
Def | DefI => return Shape::Definition { words: 4 },
DefB => return Shape::Definition { words: 1 },
Phase => return Shape::Phase,
Nop | Ret | EndLoop | EndRep | Else | EndIf | Break => (false, 0),
Call | Label | Rep | If | BreakP => (false, 1),
CallNz | Loop | IfC | BreakC => (false, 2),
TexKill | TexDepth => (true, 0),
Tex if sm1 => (true, usize::from(version.minor == 4)),
Tex => (true, 2),
TexCoord => (true, usize::from(version.minor == 4)),
Sgn | SinCos if !sm3 => (true, 3),
Mov | Rcp | Rsq | Exp | Log | Lit | Frc | Abs | Nrm | Sgn | SinCos | Mova | ExpP | LogP | Dsx
| Dsy | TexBem | TexBemL | TexReg2Ar | TexReg2Gb | TexM3x2Pad | TexM3x2Tex | TexM3x3Pad
| TexM3x3Tex | TexM3x3VSpec | TexReg2Rgb | TexDp3Tex | TexM3x2Depth | TexDp3 | TexM3x3 => {
(true, 1)
}
Add | Sub | Mul | Dp3 | Dp4 | Min | Max | Slt | Sge | Dst | M4x4 | M4x3 | M3x4 | M3x3 | M3x2
| Pow | Crs | TexM3x3Spec | Bem | SetP | TexLdl => (true, 2),
Mad | Lrp | Cnd | Cmp | Dp2Add => (true, 3),
TexLdd => (true, 4),
};
Shape::Operands { destination, sources }
}
}