1use crate::opcodes::Shape;
16use crate::{ConstantTable, DebugInfo, Opcode, Result, Stage, Version, error};
17
18const MAX_WORDS: usize = 1_048_576;
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum RegisterType {
26 Temp,
28 Input,
30 Const,
32 Address,
34 Texture,
36 RasterizerOut,
38 AttributeOut,
40 Output,
42 ConstInt,
44 ColorOut,
46 DepthOut,
48 Sampler,
50 Const2,
52 Const3,
54 Const4,
56 ConstBool,
58 Loop,
60 Misc,
62 Label,
64 Predicate,
66}
67
68impl RegisterType {
69 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 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
128pub struct Register {
129 pub kind: RegisterType,
131 pub number: u32,
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
137pub enum Modifier {
138 None,
140 Negate,
142 Bias,
144 BiasNegate,
146 Sign,
148 SignNegate,
150 Complement,
152 X2,
154 X2Negate,
156 DivideZ,
158 DivideW,
160 Abs,
162 AbsNegate,
164 Not,
166}
167
168impl Modifier {
169 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
214pub struct Relative {
215 pub word: Option<usize>,
218 pub register: Register,
220 pub swizzle: [u8; 4],
224}
225
226#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
231pub enum Precision {
232 #[default]
234 Default,
235 Min16,
237 Min2_8,
239 Unnamed,
241}
242
243impl Precision {
244 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
257pub struct Destination {
258 pub word: usize,
260 pub register: Register,
262 pub mask: u8,
264 pub saturate: bool,
266 pub partial_precision: bool,
268 pub centroid: bool,
270 pub shift: i8,
273 pub relative: Option<Relative>,
275 pub precision: Precision,
277}
278
279#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
281pub struct Source {
282 pub word: usize,
284 pub register: Register,
286 pub swizzle: [u8; 4],
288 pub modifier: Modifier,
290 pub relative: Option<Relative>,
292 pub precision: Precision,
294}
295
296#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
299pub enum Comparison {
300 Greater,
302 Equal,
304 GreaterEqual,
306 Less,
308 NotEqual,
310 LessEqual,
312}
313
314#[derive(Clone, Debug, PartialEq, Eq)]
316pub struct Instruction {
317 pub word: usize,
319 pub opcode: Opcode,
321 pub control: u8,
324 pub coissue: bool,
326 pub predicate: Option<Source>,
329 pub destination: Option<Destination>,
331 pub sources: Vec<Source>,
333}
334
335impl Instruction {
336 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
354pub enum TextureType {
355 D2,
357 Cube,
359 Volume,
361}
362
363#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
365pub enum Declared {
366 Sampler(TextureType),
368 Semantic {
373 usage: u8,
375 index: u8,
377 },
378}
379
380#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
382pub struct Declaration {
383 pub word: usize,
385 pub token: u32,
387 pub declared: Declared,
389 pub destination: Destination,
391}
392
393#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
395pub enum Literal {
396 Float([u32; 4]),
398 Integer([i32; 4]),
400 Bool(u32),
402}
403
404#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
406pub struct Definition {
407 pub word: usize,
409 pub destination: Destination,
411 pub literal: Literal,
413}
414
415#[derive(Clone, Debug, PartialEq, Eq)]
417pub struct Comment {
418 pub word: usize,
420 pub data: Vec<u32>,
422}
423
424#[derive(Clone, Debug, PartialEq, Eq)]
426pub enum Item {
427 Comment(Comment),
429 Declaration(Declaration),
431 Definition(Definition),
433 Instruction(Instruction),
435}
436
437impl Item {
438 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#[derive(Clone, Debug, PartialEq, Eq)]
451pub struct Program {
452 pub version: Version,
454 pub items: Vec<Item>,
456 pub end: usize,
458 pub constants: Option<ConstantTable>,
460 pub debug: Option<DebugInfo>,
462}
463
464impl Program {
465 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 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 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 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
495pub fn read(bytes: &[u8]) -> Result<Program> {
497 collect(records(bytes)?)
498}
499
500pub 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
511pub 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 })
550}
551
552fn collect(mut records: Records<'_>) -> Result<Program> {
553 let mut items = Vec::new();
554 for item in records.by_ref() {
555 items.push(item?);
556 }
557 Ok(Program {
558 version: records.version,
559 items,
560 end: records.end.unwrap_or(0),
561 constants: records.constants,
562 debug: records.debug,
563 })
564}
565
566#[derive(Debug)]
568pub struct Records<'a> {
569 words: Words<'a>,
570 version: Version,
571 at: usize,
573 whole: bool,
575 end: Option<usize>,
576 done: bool,
577 constants: Option<ConstantTable>,
578 debug: Option<DebugInfo>,
579}
580
581impl Records<'_> {
582 pub fn version(&self) -> Version {
584 self.version
585 }
586 pub fn constants(&self) -> Option<&ConstantTable> {
588 self.constants.as_ref()
589 }
590 pub fn debug(&self) -> Option<&DebugInfo> {
592 self.debug.as_ref()
593 }
594 pub fn end(&self) -> Option<usize> {
596 self.end
597 }
598}
599
600impl Iterator for Records<'_> {
601 type Item = Result<Item>;
602
603 fn next(&mut self) -> Option<Result<Item>> {
604 if self.done {
605 return None;
606 }
607 match self.record() {
608 Ok(Some(item)) => Some(Ok(item)),
609 Ok(None) => {
610 self.done = true;
611 let words = self.words.len();
612 (self.whole && self.at != words).then(|| {
613 Err(error(
614 self.at,
615 format!("{} words after END", words.saturating_sub(self.at)),
616 ))
617 })
618 }
619 Err(e) => {
620 self.done = true;
621 Some(Err(e))
622 }
623 }
624 }
625}
626
627#[derive(Debug)]
629struct Words<'a> {
630 bytes: &'a [u8],
631}
632
633impl Words<'_> {
634 fn len(&self) -> usize {
635 (self.bytes.len() / 4).min(MAX_WORDS)
636 }
637 fn get(&self, at: usize) -> Option<u32> {
638 if at >= MAX_WORDS {
639 return None;
640 }
641 let start = at.checked_mul(4)?;
642 let word: [u8; 4] = self.bytes.get(start..start.checked_add(4)?)?.try_into().ok()?;
643 Some(u32::from_le_bytes(word))
644 }
645}
646
647fn bits(word: u32, shift: u32, mask: u32) -> u8 {
649 u8::try_from(word.checked_shr(shift).unwrap_or(0) & mask & 0xff).unwrap_or(u8::MAX)
650}
651
652fn register(token: u32, at: usize, version: Version) -> Result<Register> {
654 if token & 0x8000_0000 == 0 {
655 return Err(error(at, format!("operand token {token:#010x} lacks bit 31")));
656 }
657 let code = ((token >> 28) & 7) | ((token >> 8) & 0x18);
658 let number = token & 0x7ff;
659 let (kind, last) =
660 RegisterType::of(code, version).map_err(|why| error(at, format!("register type {code} {why}")))?;
661 if last.is_some_and(|last| number > last) {
662 return Err(error(
663 at,
664 format!(
665 "register {kind:?} {number}, where the type has registers 0 to {}",
666 last.unwrap_or(0)
667 ),
668 ));
669 }
670 Ok(Register { kind, number })
671}
672
673struct Operands<'a> {
676 words: &'a Words<'a>,
677 at: usize,
678 end: usize,
679 instruction: usize,
681 version: Version,
682}
683
684impl Operands<'_> {
685 fn next(&mut self) -> Result<(usize, u32)> {
686 let at = self.at;
687 let token = (at < self.end)
688 .then(|| self.words.get(at))
689 .flatten()
690 .ok_or_else(|| {
691 error(
692 self.instruction,
693 format!(
694 "instruction's operand span extends past {} (it ends at word {})",
695 if self.end < self.words.len() {
696 "its stated length"
697 } else {
698 "the stream"
699 },
700 self.end.min(self.words.len())
701 ),
702 )
703 })?;
704 self.at = at.saturating_add(1);
705 Ok((at, token))
706 }
707
708 fn relative(&mut self, operand: usize, what: &str) -> Result<Relative> {
710 let version = self.version;
711 if version.major == 1 {
712 return Ok(Relative {
713 word: None,
714 register: Register {
715 kind: RegisterType::Address,
716 number: 0,
717 },
718 swizzle: [0; 4],
719 });
720 }
721 let (at, token) = self.next().map_err(|e| {
722 error(
723 operand,
724 format!(
725 "relative addressing of {what} without its address token: {}",
726 e.detail
727 ),
728 )
729 })?;
730 let register = register(token & !0x2000, at, version)?;
731 if !matches!(
733 register.kind,
734 RegisterType::Address | RegisterType::Texture | RegisterType::Loop
735 ) || token & 0x2000 != 0
736 {
737 return Err(error(
738 at,
739 format!(
740 "relative address token of {what} names {register:?}{}, where it is the address register a0 or the \
741 loop counter aL",
742 if token & 0x2000 != 0 {
743 " relatively addressed itself"
744 } else {
745 ""
746 }
747 ),
748 ));
749 }
750 if (token >> 24) & 15 != 0 {
751 return Err(error(
752 at,
753 format!(
754 "relative address token of {what} has source modifier {}, where it has none",
755 (token >> 24) & 15
756 ),
757 ));
758 }
759 Ok(Relative {
760 word: Some(at),
761 register,
762 swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
763 })
764 }
765
766 fn destination(&mut self) -> Result<Destination> {
767 let (at, token) = self.next()?;
768 let version = self.version;
769 let register = register(token & !0x2000, at, version)?;
770 let modifiers = (token >> 20) & 15;
771 if modifiers & 8 != 0 {
772 return Err(error(
773 at,
774 format!("destination {token:#010x} sets result modifier bit 23"),
775 ));
776 }
777 let shift = bits(token, 24, 15);
778 let shift = if shift >= 8 {
779 i8::try_from(i16::from(shift).saturating_sub(16)).unwrap_or(0)
780 } else {
781 i8::try_from(shift).unwrap_or(0)
782 };
783 if !(-3..=3).contains(&shift) {
785 return Err(error(
786 at,
787 format!("destination {token:#010x} has result shift {shift}, where it is -3 to 3"),
788 ));
789 }
790 let relative = if token & 0x2000 != 0 {
792 Some(self.relative(at, "a destination")?)
793 } else {
794 None
795 };
796 Ok(Destination {
797 word: at,
798 register,
799 mask: bits(token, 16, 15),
800 saturate: modifiers & 1 != 0,
801 partial_precision: modifiers & 2 != 0,
802 centroid: modifiers & 4 != 0,
803 shift,
804 relative,
805 precision: Precision::of(token),
806 })
807 }
808
809 fn source(&mut self) -> Result<Source> {
810 let (at, token) = self.next()?;
811 let version = self.version;
812 let register = register(token & !0x2000, at, version)?;
813 let modifier = Modifier::of((token >> 24) & 15).ok_or_else(|| {
814 error(
815 at,
816 format!("source modifier {} is not one of 0 to 13", (token >> 24) & 15),
817 )
818 })?;
819 let relative = if token & 0x2000 != 0 {
820 Some(self.relative(at, "a source")?)
821 } else {
822 None
823 };
824 Ok(Source {
825 word: at,
826 register,
827 swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
828 modifier,
829 relative,
830 precision: Precision::of(token),
831 })
832 }
833}
834
835impl Records<'_> {
836 fn record(&mut self) -> Result<Option<Item>> {
838 let version = self.version;
839 let words = &self.words;
840 let mut at = self.at;
841 let word = at;
842 let token = words.get(word).ok_or_else(|| {
843 error(
844 word,
845 if word >= MAX_WORDS {
846 "the program exceeds 1,048,576 words without END"
847 } else {
848 "missing END"
849 },
850 )
851 })?;
852 at = at.saturating_add(1);
853 if token == 0xffff {
854 self.end = Some(word);
855 self.at = at;
856 return Ok(None);
857 }
858 let op = token & 0xffff;
859 if op == 0xfffe {
860 if token & 0x8000_0000 != 0 {
861 return Err(error(word, "COMMENT reserved bit"));
862 }
863 let count = usize::try_from((token >> 16) & 0x7fff).unwrap_or(usize::MAX);
864 let end = at.saturating_add(count);
865 let data: Vec<u32> = (at..end).map_while(|n| words.get(n)).collect();
866 if data.len() != count {
867 return Err(error(
868 word,
869 format!(
870 "instruction/comment span of {count} words extends past the stream's {} words",
871 words.len()
872 ),
873 ));
874 }
875 if let Some((&tag, rest)) = data.split_first() {
876 let bytes = || -> Vec<u8> { rest.iter().flat_map(|w| w.to_le_bytes()).collect() };
877 if tag == u32::from_le_bytes(*b"CTAB") {
878 if self.constants.is_some() {
879 return Err(error(word, "duplicate CTAB"));
880 }
881 self.constants = Some(crate::ctab::read(
882 &bytes(),
883 word.saturating_add(2),
884 version.token(),
885 )?);
886 } else if tag == u32::from_le_bytes(*b"DBUG") {
887 if self.debug.is_some() {
888 return Err(error(word, "duplicate DBUG"));
889 }
890 self.debug = Some(crate::debug::read(&bytes(), word.saturating_add(2))?);
891 }
892 }
893 self.at = end;
894 return Ok(Some(Item::Comment(Comment { word, data })));
895 }
896 let opcode = u16::try_from(op)
897 .ok()
898 .and_then(Opcode::from_code)
899 .ok_or_else(|| error(word, format!("opcode {op} is not a Direct3D 9 instruction")))?;
900 let sm1 = version.major == 1;
902 let length = usize::from(bits(token, 24, 15));
903 let shape = match (opcode, opcode.shape(version)) {
904 (Opcode::Sgn, Shape::Operands { destination, .. }) if version.major == 3 && length == 4 => {
907 Shape::Operands {
908 destination,
909 sources: 3,
910 }
911 }
912 (_, shape) => shape,
913 };
914 let predicated = token & 0x1000_0000 != 0;
915 let coissue = token & 0x4000_0000 != 0;
916 let control = bits(token, 16, 0xff);
917 if token & 0xa000_0000 != 0 {
918 return Err(error(
919 word,
920 format!("instruction token {token:#010x} sets reserved bit 29 or 31"),
921 ));
922 }
923 if shape == Shape::Phase && token != 0xfffd {
926 return Err(error(
927 word,
928 format!("phase token {token:#010x} has bits beyond the opcode"),
929 ));
930 }
931 let controlled = match opcode {
932 Opcode::Tex if !sm1 => (0..=2).contains(&control),
933 Opcode::IfC | Opcode::BreakC | Opcode::SetP => (1..=6).contains(&control),
934 _ => control == 0,
935 };
936 if !controlled {
937 return Err(error(
938 word,
939 format!(
940 "{} has control bits {control:#x}, which it does not define",
941 opcode.name()
942 ),
943 ));
944 }
945 let end = if sm1 {
946 words.len()
947 } else {
948 let end = at.saturating_add(length);
949 if end > words.len() {
950 return Err(error(
951 word,
952 format!(
953 "instruction/comment span of {length} operands extends past the stream's {} words",
954 words.len()
955 ),
956 ));
957 }
958 end
959 };
960 let mut operands = Operands {
961 words,
962 at,
963 end,
964 instruction: word,
965 version,
966 };
967 let item = match shape {
968 Shape::Phase => Item::Instruction(Instruction {
969 word,
970 opcode,
971 control,
972 coissue,
973 predicate: None,
974 destination: None,
975 sources: Vec::new(),
976 }),
977 Shape::Declaration => {
978 if predicated {
979 return Err(error(word, "predicated dcl"));
980 }
981 let (token_at, token) = operands.next()?;
982 let destination = operands.destination()?;
983 Item::Declaration(Declaration {
984 word,
985 token,
986 declared: declared(token, token_at, destination.register)?,
987 destination,
988 })
989 }
990 Shape::Definition { words: count } => {
991 if predicated {
992 return Err(error(word, format!("predicated {}", opcode.name())));
993 }
994 let destination = operands.destination()?;
995 let expected = match opcode {
996 Opcode::Def => RegisterType::Const,
997 Opcode::DefI => RegisterType::ConstInt,
998 _ => RegisterType::ConstBool,
999 };
1000 if destination.register.kind != expected || destination.relative.is_some() {
1001 return Err(error(
1002 destination.word,
1003 format!(
1004 "{} defines {:?}, where it defines a {expected:?} register",
1005 opcode.name(),
1006 destination.register
1007 ),
1008 ));
1009 }
1010 let mut values = [0u32; 4];
1011 for value in values.iter_mut().take(count) {
1012 *value = operands.next()?.1;
1013 }
1014 let literal = match opcode {
1015 Opcode::Def => Literal::Float(values),
1016 Opcode::DefI => Literal::Integer(values.map(|v| i32::from_le_bytes(v.to_le_bytes()))),
1017 _ => {
1018 let [value, ..] = values;
1019 Literal::Bool(value)
1020 }
1021 };
1022 Item::Definition(Definition {
1023 word,
1024 destination,
1025 literal,
1026 })
1027 }
1028 Shape::Operands { destination, sources } => {
1029 let destination = if destination {
1030 Some(operands.destination()?)
1031 } else {
1032 None
1033 };
1034 let predicate = if predicated {
1035 let p = operands.source()?;
1036 if p.register.kind != RegisterType::Predicate
1037 || !matches!(p.modifier, Modifier::None | Modifier::Not)
1038 {
1039 return Err(error(
1040 p.word,
1041 format!(
1042 "the predicate token names {:?} with {:?}, where it is p0 or !p0",
1043 p.register, p.modifier
1044 ),
1045 ));
1046 }
1047 Some(p)
1048 } else {
1049 None
1050 };
1051 let mut read = Vec::with_capacity(sources);
1052 for _ in 0..sources {
1053 read.push(operands.source()?);
1054 }
1055 Item::Instruction(Instruction {
1056 word,
1057 opcode,
1058 control,
1059 coissue,
1060 predicate,
1061 destination,
1062 sources: read,
1063 })
1064 }
1065 };
1066 let used = operands.at.saturating_sub(at);
1067 if sm1 {
1068 if length != 0 && length != used {
1070 return Err(error(
1071 word,
1072 format!(
1073 "{} states {length} operand tokens, where its operands take {used}",
1074 opcode.name()
1075 ),
1076 ));
1077 }
1078 } else if used != length {
1079 return Err(error(
1080 word,
1081 format!(
1082 "{} states {length} operand tokens, where its operands take {used}",
1083 opcode.name()
1084 ),
1085 ));
1086 }
1087 self.at = operands.at;
1088 Ok(Some(item))
1089 }
1090}
1091
1092fn declared(token: u32, at: usize, register: Register) -> Result<Declared> {
1094 if token & 0x8000_0000 == 0 {
1095 return Err(error(at, format!("DCL token {token:#010x} lacks bit 31")));
1096 }
1097 if register.kind == RegisterType::Sampler {
1098 return Ok(Declared::Sampler(match token {
1099 0x9000_0000 => TextureType::D2,
1100 0x9800_0000 => TextureType::Cube,
1101 0xa000_0000 => TextureType::Volume,
1102 _ => {
1103 return Err(error(
1104 at,
1105 format!(
1106 "DCL sampler texture type {token:#x}, where it is 0x90000000 (2d), 0x98000000 (cube) or \
1107 0xa0000000 (volume)"
1108 ),
1109 ));
1110 }
1111 }));
1112 }
1113 if token & !0x800f_000f != 0 {
1114 return Err(error(at, "DCL semantic reserved bits"));
1115 }
1116 let usage = bits(token, 0, 15);
1117 if usage > 13 {
1118 return Err(error(at, format!("DCL usage {usage}, where it is 0 to 13")));
1119 }
1120 Ok(Declared::Semantic {
1121 usage,
1122 index: bits(token, 16, 15),
1123 })
1124}