Skip to main content

d3d9_reader/
program.rs

1//! The token stream: records and the decoder.
2//!
3//! A program is a version token, then instruction tokens each followed by its operand tokens, then `END`
4//! (`0x0000ffff`). An instruction token holds the opcode in bits 0 to 15, opcode-specific controls in bits 16 to 23,
5//! from Shader Model 2 on the count of operand tokens that follow in bits 24 to 27, the predicate flag in bit 28 and
6//! the pixel 1.x co-issue flag in bit 30. A comment token (`0xfffe`) holds its length in words in bits 16 to 30.
7//!
8//! An operand token has bit 31 set, the register number in bits 0 to 10 and the register type split over bits 28 to 30
9//! (its low three bits) and 11 to 12 (its high two). A destination has its write mask in bits 16 to 19, its result
10//! modifiers in 20 to 23 and the pixel 1.x result shift in 24 to 27; a source its swizzle in bits 16 to 23 and its
11//! modifier in 24 to 27. Bit 13 marks a relatively addressed operand: from Shader Model 2 on a relative-address token
12//! (the address register `a0` or the loop counter `aL`, with a swizzle) follows it; in a vertex 1.x program the index
13//! is always `a0.x` and no token follows.
14
15use crate::opcodes::Shape;
16use crate::{ConstantTable, DebugInfo, Opcode, Result, Stage, Version, error};
17
18/// The most words a program may have, END included.
19const MAX_WORDS: usize = 1_048_576;
20
21/// A register type (`D3DSHADER_PARAM_REGISTER_TYPE`, `D3DSPR_*`). Type 3 is the address register in a vertex program
22/// and a texture register in a pixel program; type 6 is a vertex program's texture-coordinate output (`oTN`) before
23/// 3.0 and its output `oN` in 3.0.
24#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum RegisterType {
26    /// `rN` (`D3DSPR_TEMP`, 0).
27    Temp,
28    /// `vN` (`D3DSPR_INPUT`, 1).
29    Input,
30    /// `cN` (`D3DSPR_CONST`, 2).
31    Const,
32    /// `a0` of a vertex program (`D3DSPR_ADDR`, 3).
33    Address,
34    /// `tN` of a pixel program (`D3DSPR_TEXTURE`, 3).
35    Texture,
36    /// `oPos` (0), `oFog` (1) or `oPts` (2) of a vertex program before 3.0 (`D3DSPR_RASTOUT`, 4).
37    RasterizerOut,
38    /// `oDN`, a vertex program's colour output before 3.0 (`D3DSPR_ATTROUT`, 5).
39    AttributeOut,
40    /// `oTN` before vertex 3.0, `oN` in 3.0 (`D3DSPR_TEXCRDOUT` and `D3DSPR_OUTPUT`, 6).
41    Output,
42    /// `iN`, an integer constant (`D3DSPR_CONSTINT`, 7).
43    ConstInt,
44    /// `oCN`, a pixel program's colour output (`D3DSPR_COLOROUT`, 8).
45    ColorOut,
46    /// `oDepth` (`D3DSPR_DEPTHOUT`, 9).
47    DepthOut,
48    /// `sN` (`D3DSPR_SAMPLER`, 10).
49    Sampler,
50    /// `cN` from 2048 on, the number being `N - 2048` (`D3DSPR_CONST2`, 11).
51    Const2,
52    /// `cN` from 4096 on (`D3DSPR_CONST3`, 12).
53    Const3,
54    /// `cN` from 6144 on (`D3DSPR_CONST4`, 13).
55    Const4,
56    /// `bN`, a boolean constant (`D3DSPR_CONSTBOOL`, 14).
57    ConstBool,
58    /// `aL`, the loop counter (`D3DSPR_LOOP`, 15).
59    Loop,
60    /// `vPos` (0) or `vFace` (1) of a pixel 3.0 program (`D3DSPR_MISCTYPE`, 17).
61    Misc,
62    /// `lN`, a label (`D3DSPR_LABEL`, 18).
63    Label,
64    /// `p0`, the predicate (`D3DSPR_PREDICATE`, 19).
65    Predicate,
66}
67
68impl RegisterType {
69    /// The type's value in the token (`D3DSPR_*`).
70    pub fn code(self) -> u8 {
71        use RegisterType::*;
72        match self {
73            Temp => 0,
74            Input => 1,
75            Const => 2,
76            Address | Texture => 3,
77            RasterizerOut => 4,
78            AttributeOut => 5,
79            Output => 6,
80            ConstInt => 7,
81            ColorOut => 8,
82            DepthOut => 9,
83            Sampler => 10,
84            Const2 => 11,
85            Const3 => 12,
86            Const4 => 13,
87            ConstBool => 14,
88            Loop => 15,
89            Misc => 17,
90            Label => 18,
91            Predicate => 19,
92        }
93    }
94
95    /// The register type of a token value in `version` (whose stage tells `a0` from `tN`), and the largest register
96    /// number it has (`None` for a bank whose size is the device's), or why the value names no register. A type is
97    /// read in any version, as Microsoft's disassembler lists it.
98    fn of(code: u32, version: Version) -> std::result::Result<(Self, Option<u32>), String> {
99        use RegisterType::*;
100        Ok(match code {
101            0 => (Temp, None),
102            1 => (Input, None),
103            2 => (Const, None),
104            3 if version.stage == Stage::Vertex => (Address, Some(0)),
105            3 => (Texture, None),
106            4 => (RasterizerOut, Some(2)),
107            5 => (AttributeOut, None),
108            6 => (Output, None),
109            7 => (ConstInt, None),
110            8 => (ColorOut, None),
111            9 => (DepthOut, Some(0)),
112            10 => (Sampler, None),
113            11 => (Const2, None),
114            12 => (Const3, None),
115            13 => (Const4, None),
116            14 => (ConstBool, None),
117            15 => (Loop, Some(0)),
118            17 => (Misc, Some(1)),
119            18 => (Label, None),
120            19 => (Predicate, Some(0)),
121            _ => return Err("is not a Direct3D 9 register type".into()),
122        })
123    }
124}
125
126/// A register: its type and number.
127#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
128pub struct Register {
129    /// The register type.
130    pub kind: RegisterType,
131    /// The register number, bits 0 to 10 of the operand token.
132    pub number: u32,
133}
134
135/// A source modifier (`D3DSHADER_PARAM_SRCMOD_TYPE`, `D3DSPSM_*`), bits 24 to 27 of a source token.
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
137pub enum Modifier {
138    /// No modifier (0).
139    None,
140    /// `-r` (1).
141    Negate,
142    /// `r_bias`, `r - 0.5` (2).
143    Bias,
144    /// `-r_bias` (3).
145    BiasNegate,
146    /// `r_bx2`, `2 * (r - 0.5)` (4).
147    Sign,
148    /// `-r_bx2` (5).
149    SignNegate,
150    /// `1 - r` (6).
151    Complement,
152    /// `r_x2` (7).
153    X2,
154    /// `-r_x2` (8).
155    X2Negate,
156    /// `r_dz`, divided by its z lane (9).
157    DivideZ,
158    /// `r_dw`, divided by its w lane (10).
159    DivideW,
160    /// `r_abs` (11).
161    Abs,
162    /// `-r_abs` (12).
163    AbsNegate,
164    /// `!r`, of a boolean or predicate (13).
165    Not,
166}
167
168impl Modifier {
169    /// The modifier's value in the token.
170    pub fn code(self) -> u8 {
171        use Modifier::*;
172        match self {
173            None => 0,
174            Negate => 1,
175            Bias => 2,
176            BiasNegate => 3,
177            Sign => 4,
178            SignNegate => 5,
179            Complement => 6,
180            X2 => 7,
181            X2Negate => 8,
182            DivideZ => 9,
183            DivideW => 10,
184            Abs => 11,
185            AbsNegate => 12,
186            Not => 13,
187        }
188    }
189
190    fn of(code: u32) -> Option<Self> {
191        use Modifier::*;
192        Some(match code {
193            0 => None,
194            1 => Negate,
195            2 => Bias,
196            3 => BiasNegate,
197            4 => Sign,
198            5 => SignNegate,
199            6 => Complement,
200            7 => X2,
201            8 => X2Negate,
202            9 => DivideZ,
203            10 => DivideW,
204            11 => Abs,
205            12 => AbsNegate,
206            13 => Not,
207            _ => return Option::None,
208        })
209    }
210}
211
212/// The index register of a relatively addressed operand (`c[a0.x + 4]`, `v[aL]`).
213#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
214pub struct Relative {
215    /// The word of the relative-address token; `None` in a vertex 1.x program, whose index is `a0.x` with no token of
216    /// its own.
217    pub word: Option<usize>,
218    /// The index register: the address register `a0` or the loop counter `aL`.
219    pub register: Register,
220    /// The relative-address token's swizzle, as stored (bits 16 to 23). Compilers write the lane they index with
221    /// replicated (`a0.x` as `.xxxx`); Microsoft's assembler writes `c0[a0]` and `[aL]` with `.xyzw`. `[0, 0, 0, 0]`
222    /// in a vertex 1.x program.
223    pub swizzle: [u8; 4],
224}
225
226/// The minimum precision of an operand (`D3DSP_MIN_PRECISION`, bits 14 to 15 of a destination or source token), which
227/// fxc writes in a feature level 9 program's Direct3D 9 part for a `min16float` value. Microsoft's listing writes it
228/// after the operand: `{min16f}`, `{min2_8f}`, nothing for 3. A relative-address or predicate token's bits 14 to 15
229/// mean nothing; it ignores them.
230#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
231pub enum Precision {
232    /// 0, the precision of the register (`D3DMP_DEFAULT`).
233    #[default]
234    Default,
235    /// 1, at least 16 bits (`D3DMP_16`, `{min16f}`).
236    Min16,
237    /// 2, at least 2.8 fixed point (`D3DMP_2_8`, `{min2_8f}`).
238    Min2_8,
239    /// 3, which `d3d9types.h` does not name and the listing writes nothing for.
240    Unnamed,
241}
242
243impl Precision {
244    /// The precision of an operand token's bits 14 to 15.
245    pub fn of(token: u32) -> Self {
246        match (token >> 14) & 3 {
247            0 => Precision::Default,
248            1 => Precision::Min16,
249            2 => Precision::Min2_8,
250            _ => Precision::Unnamed,
251        }
252    }
253}
254
255/// A destination operand.
256#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
257pub struct Destination {
258    /// The word of the operand token.
259    pub word: usize,
260    /// The register written.
261    pub register: Register,
262    /// The lanes written, bit 0 x to bit 3 w (bits 16 to 19); a declaration's lanes declared.
263    pub mask: u8,
264    /// `_sat` (`D3DSPDM_SATURATE`, bit 20).
265    pub saturate: bool,
266    /// `_pp` (`D3DSPDM_PARTIALPRECISION`, bit 21).
267    pub partial_precision: bool,
268    /// `_centroid` (`D3DSPDM_MSAMPCENTROID`, bit 22).
269    pub centroid: bool,
270    /// The pixel 1.x result shift (bits 24 to 27 as a signed number): 1 `_x2`, 2 `_x4`, 3 `_x8`, -1 `_d2`, -2 `_d4`,
271    /// -3 `_d8`; 0 otherwise and in every other version.
272    pub shift: i8,
273    /// The index register, for a relatively addressed destination (`o[aL]` of a vertex 3.0 program).
274    pub relative: Option<Relative>,
275    /// The minimum precision, bits 14 to 15.
276    pub precision: Precision,
277}
278
279/// A source operand.
280#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
281pub struct Source {
282    /// The word of the operand token.
283    pub word: usize,
284    /// The register read.
285    pub register: Register,
286    /// The lane of the register (0 x to 3 w) each of the operand's x, y, z and w reads: bits 16 to 23, two per lane.
287    pub swizzle: [u8; 4],
288    /// The source modifier.
289    pub modifier: Modifier,
290    /// The index register, for a relatively addressed source.
291    pub relative: Option<Relative>,
292    /// The minimum precision, bits 14 to 15 (of a predicate, nothing the listing writes).
293    pub precision: Precision,
294}
295
296/// The comparison of `if_comp`, `break_comp` and `setp_comp` (`D3DSHADER_COMPARISON`), bits 16 to 18 of the instruction
297/// token.
298#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
299pub enum Comparison {
300    /// `_gt` (1).
301    Greater,
302    /// `_eq` (2).
303    Equal,
304    /// `_ge` (3).
305    GreaterEqual,
306    /// `_lt` (4).
307    Less,
308    /// `_ne` (5).
309    NotEqual,
310    /// `_le` (6).
311    LessEqual,
312}
313
314/// An instruction: everything but declarations, definitions, comments and `END`.
315#[derive(Clone, Debug, PartialEq, Eq)]
316pub struct Instruction {
317    /// The word of the instruction token, counted from the version token.
318    pub word: usize,
319    /// The opcode.
320    pub opcode: Opcode,
321    /// The opcode-specific controls, bits 16 to 23: for `tex` from pixel 1.4 on 0 `texld`, 1 `texldp` (projected) and 2
322    /// `texldb` (biased); for `if_comp`, `break_comp` and `setp_comp` the [`Comparison`]; 0 for every other opcode.
323    pub control: u8,
324    /// The co-issue flag of a pixel 1.x instruction (bit 30), written `+` before it.
325    pub coissue: bool,
326    /// The predicate an instruction is predicated on (bit 28 set): the predicate register `p0`, its swizzle and
327    /// [`Modifier::Not`] or none. Its token follows the destination's.
328    pub predicate: Option<Source>,
329    /// The destination; `None` for an opcode that has none. `texkill`'s is the register it tests.
330    pub destination: Option<Destination>,
331    /// The sources, in the order the stream writes them.
332    pub sources: Vec<Source>,
333}
334
335impl Instruction {
336    /// The comparison of `if_comp`, `break_comp` and `setp_comp`; `None` for other opcodes.
337    pub fn comparison(&self) -> Option<Comparison> {
338        if !matches!(self.opcode, Opcode::IfC | Opcode::BreakC | Opcode::SetP) {
339            return None;
340        }
341        Some(match self.control {
342            1 => Comparison::Greater,
343            2 => Comparison::Equal,
344            3 => Comparison::GreaterEqual,
345            4 => Comparison::Less,
346            5 => Comparison::NotEqual,
347            _ => Comparison::LessEqual,
348        })
349    }
350}
351
352/// A sampler's texture type (`D3DSAMPLER_TEXTURE_TYPE`), bits 27 to 30 of its declaration token.
353#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
354pub enum TextureType {
355    /// `dcl_2d` (2).
356    D2,
357    /// `dcl_cube` (3).
358    Cube,
359    /// `dcl_volume` (4).
360    Volume,
361}
362
363/// What a declaration token declares.
364#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
365pub enum Declared {
366    /// A sampler's texture type.
367    Sampler(TextureType),
368    /// Any other register's usage (`D3DDECLUSAGE`: 0 position, 1 blend weight, 2 blend indices, 3 normal, 4 point size,
369    /// 5 texture coordinate, 6 tangent, 7 binormal, 8 tessellation factor, 9 transformed position, 10 colour, 11 fog,
370    /// 12 depth, 13 sample; bits 0 to 3) and usage index (bits 16 to 19). A pixel 2.x program's `dcl t0` and `dcl v0`
371    /// store usage 0 and index 0, as does `dcl vPos`.
372    Semantic {
373        /// The usage.
374        usage: u8,
375        /// The usage index.
376        index: u8,
377    },
378}
379
380/// A `dcl`: the declaration token and the register declared.
381#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
382pub struct Declaration {
383    /// The word of the instruction token.
384    pub word: usize,
385    /// The declaration token, as stored.
386    pub token: u32,
387    /// What the declaration token says.
388    pub declared: Declared,
389    /// The register declared, its mask, and its modifiers (`_pp`, `_centroid`).
390    pub destination: Destination,
391}
392
393/// The literal of a definition.
394#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
395pub enum Literal {
396    /// `def`: four floats, as their bit patterns.
397    Float([u32; 4]),
398    /// `defi`: four integers.
399    Integer([i32; 4]),
400    /// `defb`: a boolean, as its word (`TRUE` is 1).
401    Bool(u32),
402}
403
404/// A `def`, `defi` or `defb`: a constant register and the literal it holds.
405#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
406pub struct Definition {
407    /// The word of the instruction token.
408    pub word: usize,
409    /// The register defined: a float, integer or boolean constant, as the opcode says.
410    pub destination: Destination,
411    /// The literal.
412    pub literal: Literal,
413}
414
415/// A comment: its words, without its token. The constant table is one, its first word the FourCC `CTAB`.
416#[derive(Clone, Debug, PartialEq, Eq)]
417pub struct Comment {
418    /// The word of the comment token.
419    pub word: usize,
420    /// The comment's words.
421    pub data: Vec<u32>,
422}
423
424/// One record of the stream.
425#[derive(Clone, Debug, PartialEq, Eq)]
426pub enum Item {
427    /// A comment.
428    Comment(Comment),
429    /// A `dcl`.
430    Declaration(Declaration),
431    /// A `def`, `defi` or `defb`.
432    Definition(Definition),
433    /// Any other instruction.
434    Instruction(Instruction),
435}
436
437impl Item {
438    /// The word of the record's token.
439    pub fn word(&self) -> usize {
440        match self {
441            Item::Comment(c) => c.word,
442            Item::Declaration(d) => d.word,
443            Item::Definition(d) => d.word,
444            Item::Instruction(i) => i.word,
445        }
446    }
447}
448
449/// A program read: its version, its records in stream order, its constant table and its debug information.
450#[derive(Clone, Debug, PartialEq, Eq)]
451pub struct Program {
452    /// The version token.
453    pub version: Version,
454    /// Every record between the version token and `END`, in stream order.
455    pub items: Vec<Item>,
456    /// The word of `END`.
457    pub end: usize,
458    /// The `CTAB` comment decoded; `None` when the program has none.
459    pub constants: Option<ConstantTable>,
460    /// The `DBUG` comment decoded (a `/Zi` build's); `None` when the program has none.
461    pub debug: Option<DebugInfo>,
462}
463
464impl Program {
465    /// The comments, in stream order.
466    pub fn comments(&self) -> impl Iterator<Item = &Comment> {
467        self.items.iter().filter_map(|i| match i {
468            Item::Comment(c) => Some(c),
469            _ => None,
470        })
471    }
472    /// The declarations, in stream order.
473    pub fn declarations(&self) -> impl Iterator<Item = &Declaration> {
474        self.items.iter().filter_map(|i| match i {
475            Item::Declaration(d) => Some(d),
476            _ => None,
477        })
478    }
479    /// The definitions, in stream order.
480    pub fn definitions(&self) -> impl Iterator<Item = &Definition> {
481        self.items.iter().filter_map(|i| match i {
482            Item::Definition(d) => Some(d),
483            _ => None,
484        })
485    }
486    /// The instructions, in stream order.
487    pub fn instructions(&self) -> impl Iterator<Item = &Instruction> {
488        self.items.iter().filter_map(|i| match i {
489            Item::Instruction(i) => Some(i),
490            _ => None,
491        })
492    }
493}
494
495/// Read a complete program: version token to `END`, with nothing after it. The bytes are little-endian words.
496pub fn read(bytes: &[u8]) -> Result<Program> {
497    collect(records(bytes)?)
498}
499
500/// Read the first program of `bytes`, which may hold more after it (a container's other records, say), and return it
501/// with its exact bytes, version token to `END`. `END` is found by reading every record, never by searching for it
502/// inside operands or comments.
503pub fn frame(bytes: &[u8]) -> Result<(Program, &[u8])> {
504    let program = collect(start(bytes, false)?)?;
505    let span = bytes
506        .get(..program.end.saturating_add(1).saturating_mul(4))
507        .ok_or_else(|| error(program.end, "END is past the buffer"))?;
508    Ok((program, span))
509}
510
511/// The records of a complete program, one at a time in stream order, for a caller that stops at the first record it
512/// cannot use, before the reader has read further (and refused, maybe, something later). The version is read first;
513/// a refusal ends the iteration, as does `END`, after which words left over are a refusal. [`read`] is these records
514/// collected.
515pub fn records(bytes: &[u8]) -> Result<Records<'_>> {
516    if !bytes.len().is_multiple_of(4) || bytes.len() > MAX_WORDS.saturating_mul(4) {
517        return Err(error(
518            0,
519            format!(
520                "token stream of {} bytes is not whole words or exceeds 1,048,576 words",
521                bytes.len()
522            ),
523        ));
524    }
525    start(bytes, true)
526}
527
528fn start(bytes: &[u8], whole: bool) -> Result<Records<'_>> {
529    let words = Words { bytes };
530    let token = words.get(0).ok_or_else(|| error(0, "no version token"))?;
531    let version = Version::from_token(token).ok_or_else(|| {
532        error(
533            0,
534            format!(
535                "version token {token:#010x} is not a Direct3D 9 vertex (0xfffe) or pixel (0xffff) program of \
536                 version 1.0 to 3.0"
537            ),
538        )
539    })?;
540    Ok(Records {
541        words,
542        version,
543        at: 1,
544        whole,
545        end: None,
546        done: false,
547        constants: None,
548        debug: None,
549        unlocated: Vec::new(),
550        located: 0,
551    })
552}
553
554fn collect(mut records: Records<'_>) -> Result<Program> {
555    let mut items = Vec::new();
556    for item in records.by_ref() {
557        items.push(item?);
558    }
559    Ok(Program {
560        version: records.version,
561        items,
562        end: records.end.unwrap_or(0),
563        constants: records.constants,
564        debug: records.debug,
565    })
566}
567
568/// A program's records in stream order: see [`records`].
569#[derive(Debug)]
570pub struct Records<'a> {
571    words: Words<'a>,
572    version: Version,
573    /// The word of the next record's token.
574    at: usize,
575    /// True when the program is the whole buffer, so that words after `END` are a refusal.
576    whole: bool,
577    end: Option<usize>,
578    done: bool,
579    constants: Option<ConstantTable>,
580    debug: Option<DebugInfo>,
581    /// The words of the declarations, definitions and instructions read and not yet checked against their line
582    /// records: those before the `DBUG` comment (all of them in a program without one).
583    unlocated: Vec<usize>,
584    /// The line records checked.
585    located: usize,
586}
587
588impl Records<'_> {
589    /// The program's version.
590    pub fn version(&self) -> Version {
591        self.version
592    }
593    /// The constant table, once the `CTAB` comment has been read.
594    pub fn constants(&self) -> Option<&ConstantTable> {
595        self.constants.as_ref()
596    }
597    /// The debug information, once the `DBUG` comment has been read.
598    pub fn debug(&self) -> Option<&DebugInfo> {
599        self.debug.as_ref()
600    }
601    /// The word of `END`, once it has been read.
602    pub fn end(&self) -> Option<usize> {
603        self.end
604    }
605}
606
607impl Iterator for Records<'_> {
608    type Item = Result<Item>;
609
610    fn next(&mut self) -> Option<Result<Item>> {
611        if self.done {
612            return None;
613        }
614        let read = self.record().and_then(|item| {
615            match &item {
616                Some(Item::Comment(_)) => {}
617                Some(other) => self.unlocated.push(other.word()),
618                None => self.located_all()?,
619            }
620            self.locate()?;
621            Ok(item)
622        });
623        match read {
624            Ok(Some(item)) => Some(Ok(item)),
625            Ok(None) => {
626                self.done = true;
627                let words = self.words.len();
628                (self.whole && self.at != words).then(|| {
629                    Err(error(
630                        self.at,
631                        format!("{} words after END", words.saturating_sub(self.at)),
632                    ))
633                })
634            }
635            Err(e) => {
636                self.done = true;
637                Some(Err(e))
638            }
639        }
640    }
641}
642
643impl Records<'_> {
644    /// Checks the declarations, definitions and instructions read against the `DBUG` comment's line records, once it
645    /// has been read. fxc writes a record for each, in stream order, with its byte offset; Microsoft's disassembler
646    /// lists a table that breaks this in ways that follow no one rule (a record's offset past the program can drop
647    /// every note and `#line`, or one `#line` and none of the notes), so such a table is refused.
648    fn locate(&mut self) -> Result<()> {
649        let Some(debug) = &self.debug else {
650            return Ok(());
651        };
652        for word in std::mem::take(&mut self.unlocated) {
653            let n = self.located;
654            let offset = word.saturating_mul(4);
655            match debug.lines.get(n) {
656                Some(line) if usize::try_from(line.offset).is_ok_and(|o| o == offset) => {}
657                Some(line) => {
658                    return Err(error(
659                        word,
660                        format!(
661                            "DBUG line record {n} has offset {:#x}, where its record starts at {offset:#x}",
662                            line.offset
663                        ),
664                    ));
665                }
666                None => {
667                    return Err(error(
668                        word,
669                        format!(
670                            "DBUG has {} line records, none for this record",
671                            debug.lines.len()
672                        ),
673                    ));
674                }
675            }
676            self.located = n.saturating_add(1);
677        }
678        Ok(())
679    }
680
681    /// At `END`: whether every line record has had its record.
682    fn located_all(&mut self) -> Result<()> {
683        self.locate()?;
684        match &self.debug {
685            Some(debug) if debug.lines.len() != self.located => Err(error(
686                self.at.saturating_sub(1),
687                format!(
688                    "DBUG has {} line records, where the program has {} declarations, definitions and instructions",
689                    debug.lines.len(),
690                    self.located
691                ),
692            )),
693            _ => Ok(()),
694        }
695    }
696}
697
698/// The stream's words, read from the bytes as needed; a buffer's bytes after its last whole word are never read.
699#[derive(Debug)]
700struct Words<'a> {
701    bytes: &'a [u8],
702}
703
704impl Words<'_> {
705    fn len(&self) -> usize {
706        (self.bytes.len() / 4).min(MAX_WORDS)
707    }
708    fn get(&self, at: usize) -> Option<u32> {
709        if at >= MAX_WORDS {
710            return None;
711        }
712        let start = at.checked_mul(4)?;
713        let word: [u8; 4] = self.bytes.get(start..start.checked_add(4)?)?.try_into().ok()?;
714        Some(u32::from_le_bytes(word))
715    }
716}
717
718/// The bits of `word` from `shift` that `mask` keeps, which is at most 8 bits wide.
719fn bits(word: u32, shift: u32, mask: u32) -> u8 {
720    u8::try_from(word.checked_shr(shift).unwrap_or(0) & mask & 0xff).unwrap_or(u8::MAX)
721}
722
723/// The register an operand token names (its bits 14 to 15, a precision, are the caller's).
724fn register(token: u32, at: usize, version: Version) -> Result<Register> {
725    if token & 0x8000_0000 == 0 {
726        return Err(error(at, format!("operand token {token:#010x} lacks bit 31")));
727    }
728    let code = ((token >> 28) & 7) | ((token >> 8) & 0x18);
729    let number = token & 0x7ff;
730    let (kind, last) =
731        RegisterType::of(code, version).map_err(|why| error(at, format!("register type {code} {why}")))?;
732    if last.is_some_and(|last| number > last) {
733        return Err(error(
734            at,
735            format!(
736                "register {kind:?} {number}, where the type has registers 0 to {}",
737                last.unwrap_or(0)
738            ),
739        ));
740    }
741    Ok(Register { kind, number })
742}
743
744/// Reads the operand tokens of one instruction, from the word after the instruction token up to `end` (the
745/// instruction's length from Shader Model 2 on; the stream's end in 1.x, whose instructions do not state theirs).
746struct Operands<'a> {
747    words: &'a Words<'a>,
748    at: usize,
749    end: usize,
750    /// The instruction token's word, which a truncation names.
751    instruction: usize,
752    version: Version,
753}
754
755impl Operands<'_> {
756    fn next(&mut self) -> Result<(usize, u32)> {
757        let at = self.at;
758        let token = (at < self.end)
759            .then(|| self.words.get(at))
760            .flatten()
761            .ok_or_else(|| {
762                error(
763                    self.instruction,
764                    format!(
765                        "instruction's operand span extends past {} (it ends at word {})",
766                        if self.end < self.words.len() {
767                            "its stated length"
768                        } else {
769                            "the stream"
770                        },
771                        self.end.min(self.words.len())
772                    ),
773                )
774            })?;
775        self.at = at.saturating_add(1);
776        Ok((at, token))
777    }
778
779    /// The relative-address token after an operand token with bit 13 set, or the implicit `a0.x` of vertex 1.x.
780    fn relative(&mut self, operand: usize, what: &str) -> Result<Relative> {
781        let version = self.version;
782        if version.major == 1 {
783            return Ok(Relative {
784                word: None,
785                register: Register {
786                    kind: RegisterType::Address,
787                    number: 0,
788                },
789                swizzle: [0; 4],
790            });
791        }
792        let (at, token) = self.next().map_err(|e| {
793            error(
794                operand,
795                format!(
796                    "relative addressing of {what} without its address token: {}",
797                    e.detail
798                ),
799            )
800        })?;
801        let register = register(token & !0x2000, at, version)?;
802        // type 3 is the address register `a0`, or `tN` in a pixel program, which Microsoft's disassembler lists as one
803        if !matches!(
804            register.kind,
805            RegisterType::Address | RegisterType::Texture | RegisterType::Loop
806        ) || token & 0x2000 != 0
807        {
808            return Err(error(
809                at,
810                format!(
811                    "relative address token of {what} names {register:?}{}, where it is the address register a0 or the \
812                     loop counter aL",
813                    if token & 0x2000 != 0 {
814                        " relatively addressed itself"
815                    } else {
816                        ""
817                    }
818                ),
819            ));
820        }
821        if (token >> 24) & 15 != 0 {
822            return Err(error(
823                at,
824                format!(
825                    "relative address token of {what} has source modifier {}, where it has none",
826                    (token >> 24) & 15
827                ),
828            ));
829        }
830        Ok(Relative {
831            word: Some(at),
832            register,
833            swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
834        })
835    }
836
837    fn destination(&mut self) -> Result<Destination> {
838        let (at, token) = self.next()?;
839        let version = self.version;
840        let register = register(token & !0x2000, at, version)?;
841        let modifiers = (token >> 20) & 15;
842        if modifiers & 8 != 0 {
843            return Err(error(
844                at,
845                format!("destination {token:#010x} sets result modifier bit 23"),
846            ));
847        }
848        let shift = bits(token, 24, 15);
849        let shift = if shift >= 8 {
850            i8::try_from(i16::from(shift).saturating_sub(16)).unwrap_or(0)
851        } else {
852            i8::try_from(shift).unwrap_or(0)
853        };
854        // pixel 1.x's shifts are read in any version, as Microsoft's disassembler lists them; 4 to 12 are no shift
855        if !(-3..=3).contains(&shift) {
856            return Err(error(
857                at,
858                format!("destination {token:#010x} has result shift {shift}, where it is -3 to 3"),
859            ));
860        }
861        // relative addressing too, which in Shader Model 1 has no address token
862        let relative = if token & 0x2000 != 0 {
863            Some(self.relative(at, "a destination")?)
864        } else {
865            None
866        };
867        Ok(Destination {
868            word: at,
869            register,
870            mask: bits(token, 16, 15),
871            saturate: modifiers & 1 != 0,
872            partial_precision: modifiers & 2 != 0,
873            centroid: modifiers & 4 != 0,
874            shift,
875            relative,
876            precision: Precision::of(token),
877        })
878    }
879
880    fn source(&mut self) -> Result<Source> {
881        let (at, token) = self.next()?;
882        let version = self.version;
883        let register = register(token & !0x2000, at, version)?;
884        let modifier = Modifier::of((token >> 24) & 15).ok_or_else(|| {
885            error(
886                at,
887                format!("source modifier {} is not one of 0 to 13", (token >> 24) & 15),
888            )
889        })?;
890        let relative = if token & 0x2000 != 0 {
891            Some(self.relative(at, "a source")?)
892        } else {
893            None
894        };
895        Ok(Source {
896            word: at,
897            register,
898            swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
899            modifier,
900            relative,
901            precision: Precision::of(token),
902        })
903    }
904}
905
906impl Records<'_> {
907    /// The next record, `None` at `END`.
908    fn record(&mut self) -> Result<Option<Item>> {
909        let version = self.version;
910        let words = &self.words;
911        let mut at = self.at;
912        let word = at;
913        let token = words.get(word).ok_or_else(|| {
914            error(
915                word,
916                if word >= MAX_WORDS {
917                    "the program exceeds 1,048,576 words without END"
918                } else {
919                    "missing END"
920                },
921            )
922        })?;
923        at = at.saturating_add(1);
924        if token == 0xffff {
925            self.end = Some(word);
926            self.at = at;
927            return Ok(None);
928        }
929        let op = token & 0xffff;
930        if op == 0xfffe {
931            if token & 0x8000_0000 != 0 {
932                return Err(error(word, "COMMENT reserved bit"));
933            }
934            let count = usize::try_from((token >> 16) & 0x7fff).unwrap_or(usize::MAX);
935            let end = at.saturating_add(count);
936            let data: Vec<u32> = (at..end).map_while(|n| words.get(n)).collect();
937            if data.len() != count {
938                return Err(error(
939                    word,
940                    format!(
941                        "instruction/comment span of {count} words extends past the stream's {} words",
942                        words.len()
943                    ),
944                ));
945            }
946            if let Some((&tag, rest)) = data.split_first() {
947                let bytes = || -> Vec<u8> { rest.iter().flat_map(|w| w.to_le_bytes()).collect() };
948                if tag == u32::from_le_bytes(*b"CTAB") {
949                    if self.constants.is_some() {
950                        return Err(error(word, "duplicate CTAB"));
951                    }
952                    self.constants = Some(crate::ctab::read(
953                        &bytes(),
954                        word.saturating_add(2),
955                        version.token(),
956                    )?);
957                } else if tag == u32::from_le_bytes(*b"DBUG") {
958                    if self.debug.is_some() {
959                        return Err(error(word, "duplicate DBUG"));
960                    }
961                    self.debug = Some(crate::debug::read(&bytes(), word.saturating_add(2))?);
962                }
963            }
964            self.at = end;
965            return Ok(Some(Item::Comment(Comment { word, data })));
966        }
967        let opcode = u16::try_from(op)
968            .ok()
969            .and_then(Opcode::from_code)
970            .ok_or_else(|| error(word, format!("opcode {op} is not a Direct3D 9 instruction")))?;
971        // an opcode outside its versions is read with the operands it takes there, as Microsoft's disassembler lists it
972        let sm1 = version.major == 1;
973        let length = usize::from(bits(token, 24, 15));
974        let shape = match (opcode, opcode.shape(version)) {
975            // 3.0's `sgn` reads one source, but Microsoft's assembler writes the three of 2.x (`sgn r0, c0, r1, r2`):
976            // the stated length tells which
977            (Opcode::Sgn, Shape::Operands { destination, .. }) if version.major == 3 && length == 4 => {
978                Shape::Operands {
979                    destination,
980                    sources: 3,
981                }
982            }
983            (_, shape) => shape,
984        };
985        let predicated = token & 0x1000_0000 != 0;
986        let coissue = token & 0x4000_0000 != 0;
987        let control = bits(token, 16, 0xff);
988        if token & 0xa000_0000 != 0 {
989            return Err(error(
990                word,
991                format!("instruction token {token:#010x} sets reserved bit 29 or 31"),
992            ));
993        }
994        // co-issue (pixel 1.x's) and predication (2.x's and 3.0's) are read in any version, as Microsoft's
995        // disassembler lists them
996        if shape == Shape::Phase && token != 0xfffd {
997            return Err(error(
998                word,
999                format!("phase token {token:#010x} has bits beyond the opcode"),
1000            ));
1001        }
1002        let controlled = match opcode {
1003            Opcode::Tex if !sm1 => (0..=2).contains(&control),
1004            Opcode::IfC | Opcode::BreakC | Opcode::SetP => (1..=6).contains(&control),
1005            _ => control == 0,
1006        };
1007        if !controlled {
1008            return Err(error(
1009                word,
1010                format!(
1011                    "{} has control bits {control:#x}, which it does not define",
1012                    opcode.name()
1013                ),
1014            ));
1015        }
1016        let end = if sm1 {
1017            words.len()
1018        } else {
1019            let end = at.saturating_add(length);
1020            if end > words.len() {
1021                return Err(error(
1022                    word,
1023                    format!(
1024                        "instruction/comment span of {length} operands extends past the stream's {} words",
1025                        words.len()
1026                    ),
1027                ));
1028            }
1029            end
1030        };
1031        let mut operands = Operands {
1032            words,
1033            at,
1034            end,
1035            instruction: word,
1036            version,
1037        };
1038        let item = match shape {
1039            Shape::Phase => Item::Instruction(Instruction {
1040                word,
1041                opcode,
1042                control,
1043                coissue,
1044                predicate: None,
1045                destination: None,
1046                sources: Vec::new(),
1047            }),
1048            Shape::Declaration => {
1049                if predicated {
1050                    return Err(error(word, "predicated dcl"));
1051                }
1052                let (token_at, token) = operands.next()?;
1053                let destination = operands.destination()?;
1054                Item::Declaration(Declaration {
1055                    word,
1056                    token,
1057                    declared: declared(token, token_at, destination.register)?,
1058                    destination,
1059                })
1060            }
1061            Shape::Definition { words: count } => {
1062                if predicated {
1063                    return Err(error(word, format!("predicated {}", opcode.name())));
1064                }
1065                let destination = operands.destination()?;
1066                let expected = match opcode {
1067                    Opcode::Def => RegisterType::Const,
1068                    Opcode::DefI => RegisterType::ConstInt,
1069                    _ => RegisterType::ConstBool,
1070                };
1071                if destination.register.kind != expected || destination.relative.is_some() {
1072                    return Err(error(
1073                        destination.word,
1074                        format!(
1075                            "{} defines {:?}, where it defines a {expected:?} register",
1076                            opcode.name(),
1077                            destination.register
1078                        ),
1079                    ));
1080                }
1081                let mut values = [0u32; 4];
1082                for value in values.iter_mut().take(count) {
1083                    *value = operands.next()?.1;
1084                }
1085                let literal = match opcode {
1086                    Opcode::Def => Literal::Float(values),
1087                    Opcode::DefI => Literal::Integer(values.map(|v| i32::from_le_bytes(v.to_le_bytes()))),
1088                    _ => {
1089                        let [value, ..] = values;
1090                        Literal::Bool(value)
1091                    }
1092                };
1093                Item::Definition(Definition {
1094                    word,
1095                    destination,
1096                    literal,
1097                })
1098            }
1099            Shape::Operands { destination, sources } => {
1100                let destination = if destination {
1101                    Some(operands.destination()?)
1102                } else {
1103                    None
1104                };
1105                let predicate = if predicated {
1106                    let p = operands.source()?;
1107                    if p.register.kind != RegisterType::Predicate
1108                        || !matches!(p.modifier, Modifier::None | Modifier::Not)
1109                    {
1110                        return Err(error(
1111                            p.word,
1112                            format!(
1113                                "the predicate token names {:?} with {:?}, where it is p0 or !p0",
1114                                p.register, p.modifier
1115                            ),
1116                        ));
1117                    }
1118                    Some(p)
1119                } else {
1120                    None
1121                };
1122                let mut read = Vec::with_capacity(sources);
1123                for _ in 0..sources {
1124                    read.push(operands.source()?);
1125                }
1126                Item::Instruction(Instruction {
1127                    word,
1128                    opcode,
1129                    control,
1130                    coissue,
1131                    predicate,
1132                    destination,
1133                    sources: read,
1134                })
1135            }
1136        };
1137        let used = operands.at.saturating_sub(at);
1138        if sm1 {
1139            // a 1.x instruction states no length; one that states one must state its own
1140            if length != 0 && length != used {
1141                return Err(error(
1142                    word,
1143                    format!(
1144                        "{} states {length} operand tokens, where its operands take {used}",
1145                        opcode.name()
1146                    ),
1147                ));
1148            }
1149        } else if used != length {
1150            return Err(error(
1151                word,
1152                format!(
1153                    "{} states {length} operand tokens, where its operands take {used}",
1154                    opcode.name()
1155                ),
1156            ));
1157        }
1158        self.at = operands.at;
1159        Ok(Some(item))
1160    }
1161}
1162
1163/// What a declaration token declares for `register`.
1164fn declared(token: u32, at: usize, register: Register) -> Result<Declared> {
1165    if token & 0x8000_0000 == 0 {
1166        return Err(error(at, format!("DCL token {token:#010x} lacks bit 31")));
1167    }
1168    if register.kind == RegisterType::Sampler {
1169        return Ok(Declared::Sampler(match token {
1170            0x9000_0000 => TextureType::D2,
1171            0x9800_0000 => TextureType::Cube,
1172            0xa000_0000 => TextureType::Volume,
1173            _ => {
1174                return Err(error(
1175                    at,
1176                    format!(
1177                        "DCL sampler texture type {token:#x}, where it is 0x90000000 (2d), 0x98000000 (cube) or \
1178                         0xa0000000 (volume)"
1179                    ),
1180                ));
1181            }
1182        }));
1183    }
1184    if token & !0x800f_000f != 0 {
1185        return Err(error(at, "DCL semantic reserved bits"));
1186    }
1187    let usage = bits(token, 0, 15);
1188    if usage > 13 {
1189        return Err(error(at, format!("DCL usage {usage}, where it is 0 to 13")));
1190    }
1191    Ok(Declared::Semantic {
1192        usage,
1193        index: bits(token, 16, 15),
1194    })
1195}