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 sm1 = version.major == 1;
906        let length = usize::from(bits(token, 24, 15));
907        let shape = match (opcode, opcode.shape(version)) {
908            // 3.0's `sgn` reads one source, but Microsoft's assembler writes the three of 2.x (`sgn r0, c0, r1, r2`):
909            // the stated length tells which
910            (Opcode::Sgn, Shape::Operands { destination, .. }) if version.major == 3 && length == 4 => {
911                Shape::Operands {
912                    destination,
913                    sources: 3,
914                }
915            }
916            (_, shape) => shape,
917        };
918        let predicated = token & 0x1000_0000 != 0;
919        let coissue = token & 0x4000_0000 != 0;
920        let control = bits(token, 16, 0xff);
921        if token & 0xa000_0000 != 0 {
922            return Err(error(
923                word,
924                format!("instruction token {token:#010x} sets reserved bit 29 or 31"),
925            ));
926        }
927        if coissue && !(sm1 && version.stage == Stage::Pixel) {
928            return Err(error(word, "co-issue outside a pixel 1.x program"));
929        }
930        if predicated && (sm1 || (version.major == 2 && version.minor == 0)) {
931            return Err(error(
932                word,
933                format!("predication in {version}, which has no predicate register"),
934            ));
935        }
936        if shape == Shape::Phase && token != 0xfffd {
937            return Err(error(
938                word,
939                format!("phase token {token:#010x} has bits beyond the opcode"),
940            ));
941        }
942        let controlled = match opcode {
943            Opcode::Tex if !sm1 => (0..=2).contains(&control),
944            Opcode::IfC | Opcode::BreakC | Opcode::SetP => (1..=6).contains(&control),
945            _ => control == 0,
946        };
947        if !controlled {
948            return Err(error(
949                word,
950                format!(
951                    "{} has control bits {control:#x}, which it does not define",
952                    opcode.name()
953                ),
954            ));
955        }
956        let end = if sm1 {
957            words.len()
958        } else {
959            let end = at.saturating_add(length);
960            if end > words.len() {
961                return Err(error(
962                    word,
963                    format!(
964                        "instruction/comment span of {length} operands extends past the stream's {} words",
965                        words.len()
966                    ),
967                ));
968            }
969            end
970        };
971        let mut operands = Operands {
972            words,
973            at,
974            end,
975            instruction: word,
976            version,
977        };
978        let item = match shape {
979            Shape::Phase => Item::Instruction(Instruction {
980                word,
981                opcode,
982                control,
983                coissue,
984                predicate: None,
985                destination: None,
986                sources: Vec::new(),
987            }),
988            Shape::Declaration => {
989                if predicated {
990                    return Err(error(word, "predicated dcl"));
991                }
992                let (token_at, token) = operands.next()?;
993                let destination = operands.destination()?;
994                Item::Declaration(Declaration {
995                    word,
996                    token,
997                    declared: declared(token, token_at, destination.register)?,
998                    destination,
999                })
1000            }
1001            Shape::Definition { words: count } => {
1002                if predicated {
1003                    return Err(error(word, format!("predicated {}", opcode.name())));
1004                }
1005                let destination = operands.destination()?;
1006                let expected = match opcode {
1007                    Opcode::Def => RegisterType::Const,
1008                    Opcode::DefI => RegisterType::ConstInt,
1009                    _ => RegisterType::ConstBool,
1010                };
1011                if destination.register.kind != expected || destination.relative.is_some() {
1012                    return Err(error(
1013                        destination.word,
1014                        format!(
1015                            "{} defines {:?}, where it defines a {expected:?} register",
1016                            opcode.name(),
1017                            destination.register
1018                        ),
1019                    ));
1020                }
1021                let mut values = [0u32; 4];
1022                for value in values.iter_mut().take(count) {
1023                    *value = operands.next()?.1;
1024                }
1025                let literal = match opcode {
1026                    Opcode::Def => Literal::Float(values),
1027                    Opcode::DefI => Literal::Integer(values.map(|v| i32::from_le_bytes(v.to_le_bytes()))),
1028                    _ => {
1029                        let [value, ..] = values;
1030                        Literal::Bool(value)
1031                    }
1032                };
1033                Item::Definition(Definition {
1034                    word,
1035                    destination,
1036                    literal,
1037                })
1038            }
1039            Shape::Operands { destination, sources } => {
1040                let destination = if destination {
1041                    Some(operands.destination()?)
1042                } else {
1043                    None
1044                };
1045                let predicate = if predicated {
1046                    let p = operands.source()?;
1047                    if p.register.kind != RegisterType::Predicate
1048                        || !matches!(p.modifier, Modifier::None | Modifier::Not)
1049                    {
1050                        return Err(error(
1051                            p.word,
1052                            format!(
1053                                "the predicate token names {:?} with {:?}, where it is p0 or !p0",
1054                                p.register, p.modifier
1055                            ),
1056                        ));
1057                    }
1058                    Some(p)
1059                } else {
1060                    None
1061                };
1062                let mut read = Vec::with_capacity(sources);
1063                for _ in 0..sources {
1064                    read.push(operands.source()?);
1065                }
1066                Item::Instruction(Instruction {
1067                    word,
1068                    opcode,
1069                    control,
1070                    coissue,
1071                    predicate,
1072                    destination,
1073                    sources: read,
1074                })
1075            }
1076        };
1077        let used = operands.at.saturating_sub(at);
1078        if sm1 {
1079            // a 1.x instruction states no length; one that states one must state its own
1080            if length != 0 && length != used {
1081                return Err(error(
1082                    word,
1083                    format!(
1084                        "{} states {length} operand tokens, where its operands take {used}",
1085                        opcode.name()
1086                    ),
1087                ));
1088            }
1089        } else if used != length {
1090            return Err(error(
1091                word,
1092                format!(
1093                    "{} states {length} operand tokens, where its operands take {used}",
1094                    opcode.name()
1095                ),
1096            ));
1097        }
1098        self.at = operands.at;
1099        Ok(Some(item))
1100    }
1101}
1102
1103/// What a declaration token declares for `register`.
1104fn declared(token: u32, at: usize, register: Register) -> Result<Declared> {
1105    if token & 0x8000_0000 == 0 {
1106        return Err(error(at, format!("DCL token {token:#010x} lacks bit 31")));
1107    }
1108    if register.kind == RegisterType::Sampler {
1109        return Ok(Declared::Sampler(match token {
1110            0x9000_0000 => TextureType::D2,
1111            0x9800_0000 => TextureType::Cube,
1112            0xa000_0000 => TextureType::Volume,
1113            _ => {
1114                return Err(error(
1115                    at,
1116                    format!(
1117                        "DCL sampler texture type {token:#x}, where it is 0x90000000 (2d), 0x98000000 (cube) or \
1118                         0xa0000000 (volume)"
1119                    ),
1120                ));
1121            }
1122        }));
1123    }
1124    if token & !0x800f_000f != 0 {
1125        return Err(error(at, "DCL semantic reserved bits"));
1126    }
1127    let usage = bits(token, 0, 15);
1128    if usage > 13 {
1129        return Err(error(at, format!("DCL usage {usage}, where it is 0 to 13")));
1130    }
1131    Ok(Declared::Semantic {
1132        usage,
1133        index: bits(token, 16, 15),
1134    })
1135}