Skip to main content

d3d9_reader/
opcodes.rs

1//! The instruction opcodes of Direct3D 9 shader bytecode (`D3DSHADER_INSTRUCTION_OPCODE_TYPE` in `d3d9types.h`), the
2//! shader versions each belongs to and the operands each takes there.
3
4use crate::{Stage, Version};
5
6/// An instruction opcode: the low 16 bits of an instruction token. The variants carry the token value
7/// ([`Opcode::code`]) and the assembler's name ([`Opcode::name`]); declarations (`dcl`), definitions (`def`, `defi`,
8/// `defb`), comments and `end` are opcodes of the stream as well but are read into their own records.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub enum Opcode {
11    /// `nop` (0).
12    Nop,
13    /// `mov` (1).
14    Mov,
15    /// `add` (2).
16    Add,
17    /// `sub` (3).
18    Sub,
19    /// `mad` (4).
20    Mad,
21    /// `mul` (5).
22    Mul,
23    /// `rcp` (6).
24    Rcp,
25    /// `rsq` (7).
26    Rsq,
27    /// `dp3` (8).
28    Dp3,
29    /// `dp4` (9).
30    Dp4,
31    /// `min` (10).
32    Min,
33    /// `max` (11).
34    Max,
35    /// `slt` (12).
36    Slt,
37    /// `sge` (13).
38    Sge,
39    /// `exp` (14).
40    Exp,
41    /// `log` (15).
42    Log,
43    /// `lit` (16).
44    Lit,
45    /// `dst` (17).
46    Dst,
47    /// `lrp` (18).
48    Lrp,
49    /// `frc` (19).
50    Frc,
51    /// `m4x4` (20).
52    M4x4,
53    /// `m4x3` (21).
54    M4x3,
55    /// `m3x4` (22).
56    M3x4,
57    /// `m3x3` (23).
58    M3x3,
59    /// `m3x2` (24).
60    M3x2,
61    /// `call` (25): its source is the label `lN`.
62    Call,
63    /// `callnz` (26): a label, then the boolean constant `bN` or the predicate `p0` it tests.
64    CallNz,
65    /// `loop` (27): the loop counter `aL`, then the integer constant `iN` of its start, count and step.
66    Loop,
67    /// `ret` (28).
68    Ret,
69    /// `endloop` (29).
70    EndLoop,
71    /// `label` (30): its source is the label `lN`.
72    Label,
73    /// `dcl` (31), read into a [`crate::Declaration`].
74    Dcl,
75    /// `pow` (32).
76    Pow,
77    /// `crs` (33).
78    Crs,
79    /// `sgn` (34): three sources in Shader Model 2, one in 3.
80    Sgn,
81    /// `abs` (35).
82    Abs,
83    /// `nrm` (36).
84    Nrm,
85    /// `sincos` (37): three sources in Shader Model 2 (the angle and the two `D3DSINCOSCONST` registers), one in 3.
86    SinCos,
87    /// `rep` (38): its source is the integer constant `iN` whose x lane is the count.
88    Rep,
89    /// `endrep` (39).
90    EndRep,
91    /// `if` (40): its source is the boolean constant `bN` or the predicate `p0`.
92    If,
93    /// `if_comp` (41): two sources compared as [`crate::Instruction::comparison`] says.
94    IfC,
95    /// `else` (42).
96    Else,
97    /// `endif` (43).
98    EndIf,
99    /// `break` (44).
100    Break,
101    /// `break_comp` (45): two sources compared as [`crate::Instruction::comparison`] says.
102    BreakC,
103    /// `mova` (46).
104    Mova,
105    /// `defb` (47), read into a [`crate::Definition`].
106    DefB,
107    /// `defi` (48), read into a [`crate::Definition`].
108    DefI,
109    /// `texcoord` (64) before pixel 1.4, `texcrd` in 1.4.
110    TexCoord,
111    /// `texkill` (65): its destination token names the register tested.
112    TexKill,
113    /// `tex` (66) before pixel 1.4, `texld` from 1.4 on (`texldp` and `texldb` by [`crate::Instruction::control`]).
114    Tex,
115    /// `texbem` (67).
116    TexBem,
117    /// `texbeml` (68).
118    TexBemL,
119    /// `texreg2ar` (69).
120    TexReg2Ar,
121    /// `texreg2gb` (70).
122    TexReg2Gb,
123    /// `texm3x2pad` (71).
124    TexM3x2Pad,
125    /// `texm3x2tex` (72).
126    TexM3x2Tex,
127    /// `texm3x3pad` (73).
128    TexM3x3Pad,
129    /// `texm3x3tex` (74).
130    TexM3x3Tex,
131    /// `texm3x3spec` (76).
132    TexM3x3Spec,
133    /// `texm3x3vspec` (77).
134    TexM3x3VSpec,
135    /// `expp` (78).
136    ExpP,
137    /// `logp` (79).
138    LogP,
139    /// `cnd` (80).
140    Cnd,
141    /// `def` (81), read into a [`crate::Definition`].
142    Def,
143    /// `texreg2rgb` (82).
144    TexReg2Rgb,
145    /// `texdp3tex` (83).
146    TexDp3Tex,
147    /// `texm3x2depth` (84).
148    TexM3x2Depth,
149    /// `texdp3` (85).
150    TexDp3,
151    /// `texm3x3` (86).
152    TexM3x3,
153    /// `texdepth` (87).
154    TexDepth,
155    /// `cmp` (88).
156    Cmp,
157    /// `bem` (89).
158    Bem,
159    /// `dp2add` (90).
160    Dp2Add,
161    /// `dsx` (91).
162    Dsx,
163    /// `dsy` (92).
164    Dsy,
165    /// `texldd` (93).
166    TexLdd,
167    /// `setp_comp` (94): its destination is the predicate `p0`, its two sources compared as
168    /// [`crate::Instruction::comparison`] says.
169    SetP,
170    /// `texldl` (95).
171    TexLdl,
172    /// `breakp` (96): its source is the predicate `p0`.
173    BreakP,
174    /// `phase` (0xfffd): the boundary between the two phases of a pixel 1.4 program.
175    Phase,
176}
177
178/// Every opcode with its token value and name, in token order.
179const TABLE: &[(Opcode, u16, &str)] = &[
180    (Opcode::Nop, 0, "nop"),
181    (Opcode::Mov, 1, "mov"),
182    (Opcode::Add, 2, "add"),
183    (Opcode::Sub, 3, "sub"),
184    (Opcode::Mad, 4, "mad"),
185    (Opcode::Mul, 5, "mul"),
186    (Opcode::Rcp, 6, "rcp"),
187    (Opcode::Rsq, 7, "rsq"),
188    (Opcode::Dp3, 8, "dp3"),
189    (Opcode::Dp4, 9, "dp4"),
190    (Opcode::Min, 10, "min"),
191    (Opcode::Max, 11, "max"),
192    (Opcode::Slt, 12, "slt"),
193    (Opcode::Sge, 13, "sge"),
194    (Opcode::Exp, 14, "exp"),
195    (Opcode::Log, 15, "log"),
196    (Opcode::Lit, 16, "lit"),
197    (Opcode::Dst, 17, "dst"),
198    (Opcode::Lrp, 18, "lrp"),
199    (Opcode::Frc, 19, "frc"),
200    (Opcode::M4x4, 20, "m4x4"),
201    (Opcode::M4x3, 21, "m4x3"),
202    (Opcode::M3x4, 22, "m3x4"),
203    (Opcode::M3x3, 23, "m3x3"),
204    (Opcode::M3x2, 24, "m3x2"),
205    (Opcode::Call, 25, "call"),
206    (Opcode::CallNz, 26, "callnz"),
207    (Opcode::Loop, 27, "loop"),
208    (Opcode::Ret, 28, "ret"),
209    (Opcode::EndLoop, 29, "endloop"),
210    (Opcode::Label, 30, "label"),
211    (Opcode::Dcl, 31, "dcl"),
212    (Opcode::Pow, 32, "pow"),
213    (Opcode::Crs, 33, "crs"),
214    (Opcode::Sgn, 34, "sgn"),
215    (Opcode::Abs, 35, "abs"),
216    (Opcode::Nrm, 36, "nrm"),
217    (Opcode::SinCos, 37, "sincos"),
218    (Opcode::Rep, 38, "rep"),
219    (Opcode::EndRep, 39, "endrep"),
220    (Opcode::If, 40, "if"),
221    (Opcode::IfC, 41, "if_comp"),
222    (Opcode::Else, 42, "else"),
223    (Opcode::EndIf, 43, "endif"),
224    (Opcode::Break, 44, "break"),
225    (Opcode::BreakC, 45, "break_comp"),
226    (Opcode::Mova, 46, "mova"),
227    (Opcode::DefB, 47, "defb"),
228    (Opcode::DefI, 48, "defi"),
229    (Opcode::TexCoord, 64, "texcoord"),
230    (Opcode::TexKill, 65, "texkill"),
231    (Opcode::Tex, 66, "tex"),
232    (Opcode::TexBem, 67, "texbem"),
233    (Opcode::TexBemL, 68, "texbeml"),
234    (Opcode::TexReg2Ar, 69, "texreg2ar"),
235    (Opcode::TexReg2Gb, 70, "texreg2gb"),
236    (Opcode::TexM3x2Pad, 71, "texm3x2pad"),
237    (Opcode::TexM3x2Tex, 72, "texm3x2tex"),
238    (Opcode::TexM3x3Pad, 73, "texm3x3pad"),
239    (Opcode::TexM3x3Tex, 74, "texm3x3tex"),
240    (Opcode::TexM3x3Spec, 76, "texm3x3spec"),
241    (Opcode::TexM3x3VSpec, 77, "texm3x3vspec"),
242    (Opcode::ExpP, 78, "expp"),
243    (Opcode::LogP, 79, "logp"),
244    (Opcode::Cnd, 80, "cnd"),
245    (Opcode::Def, 81, "def"),
246    (Opcode::TexReg2Rgb, 82, "texreg2rgb"),
247    (Opcode::TexDp3Tex, 83, "texdp3tex"),
248    (Opcode::TexM3x2Depth, 84, "texm3x2depth"),
249    (Opcode::TexDp3, 85, "texdp3"),
250    (Opcode::TexM3x3, 86, "texm3x3"),
251    (Opcode::TexDepth, 87, "texdepth"),
252    (Opcode::Cmp, 88, "cmp"),
253    (Opcode::Bem, 89, "bem"),
254    (Opcode::Dp2Add, 90, "dp2add"),
255    (Opcode::Dsx, 91, "dsx"),
256    (Opcode::Dsy, 92, "dsy"),
257    (Opcode::TexLdd, 93, "texldd"),
258    (Opcode::SetP, 94, "setp_comp"),
259    (Opcode::TexLdl, 95, "texldl"),
260    (Opcode::BreakP, 96, "breakp"),
261    (Opcode::Phase, 0xfffd, "phase"),
262];
263
264/// The operand tokens an opcode takes in one shader version.
265#[derive(Clone, Copy, Debug, PartialEq, Eq)]
266pub(crate) enum Shape {
267    /// An optional destination, then `sources` source operands (each followed by its relative-address token where it
268    /// has one).
269    Operands { destination: bool, sources: usize },
270    /// `dcl`: the declaration token, then a destination.
271    Declaration,
272    /// `def`, `defi`, `defb`: a destination, then `words` literal words.
273    Definition { words: usize },
274    /// `phase`: no operands, and no bits but the opcode.
275    Phase,
276}
277
278/// A range of versions of one stage, both ends included: (major, minor), a software version's minor being 255.
279type Range = Option<((u8, u8), (u8, u8))>;
280
281const ALL: Range = Some(((1, 0), (3, 255)));
282const FROM_2: Range = Some(((2, 0), (3, 255)));
283const FROM_2X: Range = Some(((2, 1), (3, 255)));
284const FROM_3: Range = Some(((3, 0), (3, 255)));
285const NONE: Range = None;
286const fn pixel(from: (u8, u8), to: (u8, u8)) -> Range {
287    Some((from, to))
288}
289
290impl Opcode {
291    /// The opcode's token value (bits 0 to 15 of the instruction token).
292    pub fn code(self) -> u16 {
293        TABLE
294            .iter()
295            .find(|row| row.0 == self)
296            .map_or(u16::MAX, |row| row.1)
297    }
298
299    /// The assembler's name of the opcode, as `d3d9types.h` names it in lower case (`if_comp` for `IFC`, `setp_comp`
300    /// for `SETP`; `tex` and `texcoord` whatever the version, which the assembler spells `texld` and `texcrd` from
301    /// pixel 1.4 on).
302    pub fn name(self) -> &'static str {
303        TABLE.iter().find(|row| row.0 == self).map_or("", |row| row.2)
304    }
305
306    /// The opcode of a token value, `None` for a value Direct3D 9 does not define (75, the reserved `texm3x3diff`,
307    /// among them).
308    pub fn from_code(code: u16) -> Option<Self> {
309        TABLE.iter().find(|row| row.1 == code).map(|row| row.0)
310    }
311
312    /// The vertex and pixel versions the opcode is an instruction of, from Microsoft's instruction lists per version.
313    fn versions(self) -> (Range, Range) {
314        use Opcode::*;
315        match self {
316            Nop | Mov | Add | Sub | Mad | Mul | Dp3 | Def => (ALL, ALL),
317            Dcl => (ALL, FROM_2),
318            Rcp | Rsq | Min | Max | Exp | Log | Frc | M4x4 | M4x3 | M3x4 | M3x3 | M3x2 => (ALL, FROM_2),
319            Dp4 => (ALL, pixel((1, 2), (3, 255))),
320            Slt | Sge | Lit | Dst | ExpP | LogP => (ALL, NONE),
321            Lrp => (FROM_2, ALL),
322            Pow | Crs | Abs | Nrm | SinCos => (FROM_2, FROM_2),
323            Sgn | Mova => (FROM_2, NONE),
324            Call | CallNz | Ret | Label | Rep | EndRep | If | Else | EndIf | DefB | DefI => (FROM_2, FROM_2X),
325            Loop | EndLoop => (FROM_2, FROM_3),
326            IfC | Break | BreakC | SetP | BreakP => (FROM_2X, FROM_2X),
327            TexLdl => (FROM_3, FROM_3),
328            TexKill | Tex => (NONE, ALL),
329            TexCoord => (NONE, pixel((1, 0), (1, 4))),
330            TexBem | TexBemL | TexReg2Ar | TexReg2Gb | TexM3x2Pad | TexM3x2Tex | TexM3x3Pad | TexM3x3Tex
331            | TexM3x3Spec | TexM3x3VSpec => (NONE, pixel((1, 0), (1, 3))),
332            TexReg2Rgb | TexDp3Tex | TexDp3 | TexM3x3 => (NONE, pixel((1, 2), (1, 3))),
333            TexM3x2Depth => (NONE, pixel((1, 3), (1, 3))),
334            Cnd => (NONE, pixel((1, 0), (1, 4))),
335            Cmp => (NONE, pixel((1, 2), (3, 255))),
336            TexDepth | Bem | Phase => (NONE, pixel((1, 4), (1, 4))),
337            Dp2Add => (NONE, FROM_2),
338            Dsx | Dsy | TexLdd => (NONE, FROM_2X),
339        }
340    }
341
342    /// True when the opcode is an instruction of `version`.
343    pub fn in_version(self, version: Version) -> bool {
344        let (vertex, pixel) = self.versions();
345        let range = match version.stage {
346            Stage::Vertex => vertex,
347            Stage::Pixel => pixel,
348        };
349        range.is_some_and(|(from, to)| (from..=to).contains(&(version.major, version.minor)))
350    }
351
352    /// The operands the opcode takes in `version` (which it must be an instruction of).
353    pub(crate) fn shape(self, version: Version) -> Shape {
354        use Opcode::*;
355        let sm1 = version.major == 1;
356        let sm3 = version.major == 3;
357        let (destination, sources) = match self {
358            Dcl => return Shape::Declaration,
359            Def | DefI => return Shape::Definition { words: 4 },
360            DefB => return Shape::Definition { words: 1 },
361            Phase => return Shape::Phase,
362            Nop | Ret | EndLoop | EndRep | Else | EndIf | Break => (false, 0),
363            Call | Label | Rep | If | BreakP => (false, 1),
364            CallNz | Loop | IfC | BreakC => (false, 2),
365            TexKill | TexDepth => (true, 0),
366            // `tex t0` and `texcoord t0` name only the register they write before pixel 1.4; `texld r0, t0` and
367            // `texcrd r0, t0` read a texture coordinate register in 1.4; `texld r0, t0, s0` names the sampler from 2.0 on
368            Tex if sm1 => (true, usize::from(version.minor == 4)),
369            Tex => (true, 2),
370            TexCoord => (true, usize::from(version.minor == 4)),
371            Sgn | SinCos if !sm3 => (true, 3),
372            Mov | Rcp | Rsq | Exp | Log | Lit | Frc | Abs | Nrm | Sgn | SinCos | Mova | ExpP | LogP | Dsx
373            | Dsy | TexBem | TexBemL | TexReg2Ar | TexReg2Gb | TexM3x2Pad | TexM3x2Tex | TexM3x3Pad
374            | TexM3x3Tex | TexM3x3VSpec | TexReg2Rgb | TexDp3Tex | TexM3x2Depth | TexDp3 | TexM3x3 => {
375                (true, 1)
376            }
377            Add | Sub | Mul | Dp3 | Dp4 | Min | Max | Slt | Sge | Dst | M4x4 | M4x3 | M3x4 | M3x3 | M3x2
378            | Pow | Crs | TexM3x3Spec | Bem | SetP | TexLdl => (true, 2),
379            Mad | Lrp | Cnd | Cmp | Dp2Add => (true, 3),
380            TexLdd => (true, 4),
381        };
382        Shape::Operands { destination, sources }
383    }
384}