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