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, PartialEq, Eq, Hash)]
228pub struct Destination {
229 pub word: usize,
231 pub register: Register,
233 pub mask: u8,
235 pub saturate: bool,
237 pub partial_precision: bool,
239 pub centroid: bool,
241 pub shift: i8,
244 pub relative: Option<Relative>,
246}
247
248#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
250pub struct Source {
251 pub word: usize,
253 pub register: Register,
255 pub swizzle: [u8; 4],
257 pub modifier: Modifier,
259 pub relative: Option<Relative>,
261}
262
263#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
266pub enum Comparison {
267 Greater,
269 Equal,
271 GreaterEqual,
273 Less,
275 NotEqual,
277 LessEqual,
279}
280
281#[derive(Clone, Debug, PartialEq, Eq)]
283pub struct Instruction {
284 pub word: usize,
286 pub opcode: Opcode,
288 pub control: u8,
291 pub coissue: bool,
293 pub predicate: Option<Source>,
296 pub destination: Option<Destination>,
298 pub sources: Vec<Source>,
300}
301
302impl Instruction {
303 pub fn comparison(&self) -> Option<Comparison> {
305 if !matches!(self.opcode, Opcode::IfC | Opcode::BreakC | Opcode::SetP) {
306 return None;
307 }
308 Some(match self.control {
309 1 => Comparison::Greater,
310 2 => Comparison::Equal,
311 3 => Comparison::GreaterEqual,
312 4 => Comparison::Less,
313 5 => Comparison::NotEqual,
314 _ => Comparison::LessEqual,
315 })
316 }
317}
318
319#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
321pub enum TextureType {
322 D2,
324 Cube,
326 Volume,
328}
329
330#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
332pub enum Declared {
333 Sampler(TextureType),
335 Semantic {
340 usage: u8,
342 index: u8,
344 },
345}
346
347#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
349pub struct Declaration {
350 pub word: usize,
352 pub token: u32,
354 pub declared: Declared,
356 pub destination: Destination,
358}
359
360#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
362pub enum Literal {
363 Float([u32; 4]),
365 Integer([i32; 4]),
367 Bool(u32),
369}
370
371#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
373pub struct Definition {
374 pub word: usize,
376 pub destination: Destination,
378 pub literal: Literal,
380}
381
382#[derive(Clone, Debug, PartialEq, Eq)]
384pub struct Comment {
385 pub word: usize,
387 pub data: Vec<u32>,
389}
390
391#[derive(Clone, Debug, PartialEq, Eq)]
393pub enum Item {
394 Comment(Comment),
396 Declaration(Declaration),
398 Definition(Definition),
400 Instruction(Instruction),
402}
403
404impl Item {
405 pub fn word(&self) -> usize {
407 match self {
408 Item::Comment(c) => c.word,
409 Item::Declaration(d) => d.word,
410 Item::Definition(d) => d.word,
411 Item::Instruction(i) => i.word,
412 }
413 }
414}
415
416#[derive(Clone, Debug, PartialEq, Eq)]
418pub struct Program {
419 pub version: Version,
421 pub items: Vec<Item>,
423 pub end: usize,
425 pub constants: Option<ConstantTable>,
427 pub debug: Option<DebugInfo>,
429}
430
431impl Program {
432 pub fn comments(&self) -> impl Iterator<Item = &Comment> {
434 self.items.iter().filter_map(|i| match i {
435 Item::Comment(c) => Some(c),
436 _ => None,
437 })
438 }
439 pub fn declarations(&self) -> impl Iterator<Item = &Declaration> {
441 self.items.iter().filter_map(|i| match i {
442 Item::Declaration(d) => Some(d),
443 _ => None,
444 })
445 }
446 pub fn definitions(&self) -> impl Iterator<Item = &Definition> {
448 self.items.iter().filter_map(|i| match i {
449 Item::Definition(d) => Some(d),
450 _ => None,
451 })
452 }
453 pub fn instructions(&self) -> impl Iterator<Item = &Instruction> {
455 self.items.iter().filter_map(|i| match i {
456 Item::Instruction(i) => Some(i),
457 _ => None,
458 })
459 }
460}
461
462pub fn read(bytes: &[u8]) -> Result<Program> {
464 collect(records(bytes)?)
465}
466
467pub fn frame(bytes: &[u8]) -> Result<(Program, &[u8])> {
471 let program = collect(start(bytes, false)?)?;
472 let span = bytes
473 .get(..program.end.saturating_add(1).saturating_mul(4))
474 .ok_or_else(|| error(program.end, "END is past the buffer"))?;
475 Ok((program, span))
476}
477
478pub fn records(bytes: &[u8]) -> Result<Records<'_>> {
483 if !bytes.len().is_multiple_of(4) || bytes.len() > MAX_WORDS.saturating_mul(4) {
484 return Err(error(
485 0,
486 format!(
487 "token stream of {} bytes is not whole words or exceeds 1,048,576 words",
488 bytes.len()
489 ),
490 ));
491 }
492 start(bytes, true)
493}
494
495fn start(bytes: &[u8], whole: bool) -> Result<Records<'_>> {
496 let words = Words { bytes };
497 let token = words.get(0).ok_or_else(|| error(0, "no version token"))?;
498 let version = Version::from_token(token).ok_or_else(|| {
499 error(
500 0,
501 format!(
502 "version token {token:#010x} is not a Direct3D 9 vertex (0xfffe) or pixel (0xffff) program of \
503 version 1.0 to 3.0"
504 ),
505 )
506 })?;
507 Ok(Records {
508 words,
509 version,
510 at: 1,
511 whole,
512 end: None,
513 done: false,
514 constants: None,
515 debug: None,
516 })
517}
518
519fn collect(mut records: Records<'_>) -> Result<Program> {
520 let mut items = Vec::new();
521 for item in records.by_ref() {
522 items.push(item?);
523 }
524 Ok(Program {
525 version: records.version,
526 items,
527 end: records.end.unwrap_or(0),
528 constants: records.constants,
529 debug: records.debug,
530 })
531}
532
533#[derive(Debug)]
535pub struct Records<'a> {
536 words: Words<'a>,
537 version: Version,
538 at: usize,
540 whole: bool,
542 end: Option<usize>,
543 done: bool,
544 constants: Option<ConstantTable>,
545 debug: Option<DebugInfo>,
546}
547
548impl Records<'_> {
549 pub fn version(&self) -> Version {
551 self.version
552 }
553 pub fn constants(&self) -> Option<&ConstantTable> {
555 self.constants.as_ref()
556 }
557 pub fn debug(&self) -> Option<&DebugInfo> {
559 self.debug.as_ref()
560 }
561 pub fn end(&self) -> Option<usize> {
563 self.end
564 }
565}
566
567impl Iterator for Records<'_> {
568 type Item = Result<Item>;
569
570 fn next(&mut self) -> Option<Result<Item>> {
571 if self.done {
572 return None;
573 }
574 match self.record() {
575 Ok(Some(item)) => Some(Ok(item)),
576 Ok(None) => {
577 self.done = true;
578 let words = self.words.len();
579 (self.whole && self.at != words).then(|| {
580 Err(error(
581 self.at,
582 format!("{} words after END", words.saturating_sub(self.at)),
583 ))
584 })
585 }
586 Err(e) => {
587 self.done = true;
588 Some(Err(e))
589 }
590 }
591 }
592}
593
594#[derive(Debug)]
596struct Words<'a> {
597 bytes: &'a [u8],
598}
599
600impl Words<'_> {
601 fn len(&self) -> usize {
602 (self.bytes.len() / 4).min(MAX_WORDS)
603 }
604 fn get(&self, at: usize) -> Option<u32> {
605 if at >= MAX_WORDS {
606 return None;
607 }
608 let start = at.checked_mul(4)?;
609 let word: [u8; 4] = self.bytes.get(start..start.checked_add(4)?)?.try_into().ok()?;
610 Some(u32::from_le_bytes(word))
611 }
612}
613
614fn bits(word: u32, shift: u32, mask: u32) -> u8 {
616 u8::try_from(word.checked_shr(shift).unwrap_or(0) & mask & 0xff).unwrap_or(u8::MAX)
617}
618
619fn register(token: u32, at: usize, version: Version) -> Result<Register> {
621 if token & 0x8000_0000 == 0 {
622 return Err(error(at, format!("operand token {token:#010x} lacks bit 31")));
623 }
624 if token & 0xc000 != 0 {
625 return Err(error(
626 at,
627 format!("operand token {token:#010x} sets reserved bits 14 to 15"),
628 ));
629 }
630 let code = ((token >> 28) & 7) | ((token >> 8) & 0x18);
631 let number = token & 0x7ff;
632 let (kind, last) =
633 RegisterType::of(code, version).map_err(|why| error(at, format!("register type {code} {why}")))?;
634 if last.is_some_and(|last| number > last) {
635 return Err(error(
636 at,
637 format!(
638 "register {kind:?} {number}, where the type has registers 0 to {}",
639 last.unwrap_or(0)
640 ),
641 ));
642 }
643 Ok(Register { kind, number })
644}
645
646struct Operands<'a> {
649 words: &'a Words<'a>,
650 at: usize,
651 end: usize,
652 instruction: usize,
654 version: Version,
655}
656
657impl Operands<'_> {
658 fn next(&mut self) -> Result<(usize, u32)> {
659 let at = self.at;
660 let token = (at < self.end)
661 .then(|| self.words.get(at))
662 .flatten()
663 .ok_or_else(|| {
664 error(
665 self.instruction,
666 format!(
667 "instruction's operand span extends past {} (it ends at word {})",
668 if self.end < self.words.len() {
669 "its stated length"
670 } else {
671 "the stream"
672 },
673 self.end.min(self.words.len())
674 ),
675 )
676 })?;
677 self.at = at.saturating_add(1);
678 Ok((at, token))
679 }
680
681 fn relative(&mut self, operand: usize, what: &str) -> Result<Relative> {
683 let version = self.version;
684 if version.major == 1 {
685 return Ok(Relative {
686 word: None,
687 register: Register {
688 kind: RegisterType::Address,
689 number: 0,
690 },
691 swizzle: [0; 4],
692 });
693 }
694 let (at, token) = self.next().map_err(|e| {
695 error(
696 operand,
697 format!(
698 "relative addressing of {what} without its address token: {}",
699 e.detail
700 ),
701 )
702 })?;
703 let register = register(token & !0x2000, at, version)?;
704 if !matches!(
706 register.kind,
707 RegisterType::Address | RegisterType::Texture | RegisterType::Loop
708 ) || token & 0x2000 != 0
709 {
710 return Err(error(
711 at,
712 format!(
713 "relative address token of {what} names {register:?}{}, where it is the address register a0 or the \
714 loop counter aL",
715 if token & 0x2000 != 0 {
716 " relatively addressed itself"
717 } else {
718 ""
719 }
720 ),
721 ));
722 }
723 if (token >> 24) & 15 != 0 {
724 return Err(error(
725 at,
726 format!(
727 "relative address token of {what} has source modifier {}, where it has none",
728 (token >> 24) & 15
729 ),
730 ));
731 }
732 Ok(Relative {
733 word: Some(at),
734 register,
735 swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
736 })
737 }
738
739 fn destination(&mut self) -> Result<Destination> {
740 let (at, token) = self.next()?;
741 let version = self.version;
742 let register = register(token & !0x2000, at, version)?;
743 let modifiers = (token >> 20) & 15;
744 if modifiers & 8 != 0 {
745 return Err(error(
746 at,
747 format!("destination {token:#010x} sets result modifier bit 23"),
748 ));
749 }
750 let shift = bits(token, 24, 15);
751 let shift = if shift >= 8 {
752 i8::try_from(i16::from(shift).saturating_sub(16)).unwrap_or(0)
753 } else {
754 i8::try_from(shift).unwrap_or(0)
755 };
756 if !(-3..=3).contains(&shift) {
758 return Err(error(
759 at,
760 format!("destination {token:#010x} has result shift {shift}, where it is -3 to 3"),
761 ));
762 }
763 let relative = if token & 0x2000 != 0 {
765 Some(self.relative(at, "a destination")?)
766 } else {
767 None
768 };
769 Ok(Destination {
770 word: at,
771 register,
772 mask: bits(token, 16, 15),
773 saturate: modifiers & 1 != 0,
774 partial_precision: modifiers & 2 != 0,
775 centroid: modifiers & 4 != 0,
776 shift,
777 relative,
778 })
779 }
780
781 fn source(&mut self) -> Result<Source> {
782 let (at, token) = self.next()?;
783 let version = self.version;
784 let register = register(token & !0x2000, at, version)?;
785 let modifier = Modifier::of((token >> 24) & 15).ok_or_else(|| {
786 error(
787 at,
788 format!("source modifier {} is not one of 0 to 13", (token >> 24) & 15),
789 )
790 })?;
791 let relative = if token & 0x2000 != 0 {
792 Some(self.relative(at, "a source")?)
793 } else {
794 None
795 };
796 Ok(Source {
797 word: at,
798 register,
799 swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
800 modifier,
801 relative,
802 })
803 }
804}
805
806impl Records<'_> {
807 fn record(&mut self) -> Result<Option<Item>> {
809 let version = self.version;
810 let words = &self.words;
811 let mut at = self.at;
812 let word = at;
813 let token = words.get(word).ok_or_else(|| {
814 error(
815 word,
816 if word >= MAX_WORDS {
817 "the program exceeds 1,048,576 words without END"
818 } else {
819 "missing END"
820 },
821 )
822 })?;
823 at = at.saturating_add(1);
824 if token == 0xffff {
825 self.end = Some(word);
826 self.at = at;
827 return Ok(None);
828 }
829 let op = token & 0xffff;
830 if op == 0xfffe {
831 if token & 0x8000_0000 != 0 {
832 return Err(error(word, "COMMENT reserved bit"));
833 }
834 let count = usize::try_from((token >> 16) & 0x7fff).unwrap_or(usize::MAX);
835 let end = at.saturating_add(count);
836 let data: Vec<u32> = (at..end).map_while(|n| words.get(n)).collect();
837 if data.len() != count {
838 return Err(error(
839 word,
840 format!(
841 "instruction/comment span of {count} words extends past the stream's {} words",
842 words.len()
843 ),
844 ));
845 }
846 if let Some((&tag, rest)) = data.split_first() {
847 let bytes = || -> Vec<u8> { rest.iter().flat_map(|w| w.to_le_bytes()).collect() };
848 if tag == u32::from_le_bytes(*b"CTAB") {
849 if self.constants.is_some() {
850 return Err(error(word, "duplicate CTAB"));
851 }
852 self.constants = Some(crate::ctab::read(
853 &bytes(),
854 word.saturating_add(2),
855 version.token(),
856 )?);
857 } else if tag == u32::from_le_bytes(*b"DBUG") {
858 if self.debug.is_some() {
859 return Err(error(word, "duplicate DBUG"));
860 }
861 self.debug = Some(crate::debug::read(&bytes(), word.saturating_add(2))?);
862 }
863 }
864 self.at = end;
865 return Ok(Some(Item::Comment(Comment { word, data })));
866 }
867 let opcode = u16::try_from(op)
868 .ok()
869 .and_then(Opcode::from_code)
870 .ok_or_else(|| error(word, format!("opcode {op} is not a Direct3D 9 instruction")))?;
871 let sm1 = version.major == 1;
873 let length = usize::from(bits(token, 24, 15));
874 let shape = match (opcode, opcode.shape(version)) {
875 (Opcode::Sgn, Shape::Operands { destination, .. }) if version.major == 3 && length == 4 => {
878 Shape::Operands {
879 destination,
880 sources: 3,
881 }
882 }
883 (_, shape) => shape,
884 };
885 let predicated = token & 0x1000_0000 != 0;
886 let coissue = token & 0x4000_0000 != 0;
887 let control = bits(token, 16, 0xff);
888 if token & 0xa000_0000 != 0 {
889 return Err(error(
890 word,
891 format!("instruction token {token:#010x} sets reserved bit 29 or 31"),
892 ));
893 }
894 if shape == Shape::Phase && token != 0xfffd {
897 return Err(error(
898 word,
899 format!("phase token {token:#010x} has bits beyond the opcode"),
900 ));
901 }
902 let controlled = match opcode {
903 Opcode::Tex if !sm1 => (0..=2).contains(&control),
904 Opcode::IfC | Opcode::BreakC | Opcode::SetP => (1..=6).contains(&control),
905 _ => control == 0,
906 };
907 if !controlled {
908 return Err(error(
909 word,
910 format!(
911 "{} has control bits {control:#x}, which it does not define",
912 opcode.name()
913 ),
914 ));
915 }
916 let end = if sm1 {
917 words.len()
918 } else {
919 let end = at.saturating_add(length);
920 if end > words.len() {
921 return Err(error(
922 word,
923 format!(
924 "instruction/comment span of {length} operands extends past the stream's {} words",
925 words.len()
926 ),
927 ));
928 }
929 end
930 };
931 let mut operands = Operands {
932 words,
933 at,
934 end,
935 instruction: word,
936 version,
937 };
938 let item = match shape {
939 Shape::Phase => Item::Instruction(Instruction {
940 word,
941 opcode,
942 control,
943 coissue,
944 predicate: None,
945 destination: None,
946 sources: Vec::new(),
947 }),
948 Shape::Declaration => {
949 if predicated {
950 return Err(error(word, "predicated dcl"));
951 }
952 let (token_at, token) = operands.next()?;
953 let destination = operands.destination()?;
954 Item::Declaration(Declaration {
955 word,
956 token,
957 declared: declared(token, token_at, destination.register)?,
958 destination,
959 })
960 }
961 Shape::Definition { words: count } => {
962 if predicated {
963 return Err(error(word, format!("predicated {}", opcode.name())));
964 }
965 let destination = operands.destination()?;
966 let expected = match opcode {
967 Opcode::Def => RegisterType::Const,
968 Opcode::DefI => RegisterType::ConstInt,
969 _ => RegisterType::ConstBool,
970 };
971 if destination.register.kind != expected || destination.relative.is_some() {
972 return Err(error(
973 destination.word,
974 format!(
975 "{} defines {:?}, where it defines a {expected:?} register",
976 opcode.name(),
977 destination.register
978 ),
979 ));
980 }
981 let mut values = [0u32; 4];
982 for value in values.iter_mut().take(count) {
983 *value = operands.next()?.1;
984 }
985 let literal = match opcode {
986 Opcode::Def => Literal::Float(values),
987 Opcode::DefI => Literal::Integer(values.map(|v| i32::from_le_bytes(v.to_le_bytes()))),
988 _ => {
989 let [value, ..] = values;
990 Literal::Bool(value)
991 }
992 };
993 Item::Definition(Definition {
994 word,
995 destination,
996 literal,
997 })
998 }
999 Shape::Operands { destination, sources } => {
1000 let destination = if destination {
1001 Some(operands.destination()?)
1002 } else {
1003 None
1004 };
1005 let predicate = if predicated {
1006 let p = operands.source()?;
1007 if p.register.kind != RegisterType::Predicate
1008 || !matches!(p.modifier, Modifier::None | Modifier::Not)
1009 {
1010 return Err(error(
1011 p.word,
1012 format!(
1013 "the predicate token names {:?} with {:?}, where it is p0 or !p0",
1014 p.register, p.modifier
1015 ),
1016 ));
1017 }
1018 Some(p)
1019 } else {
1020 None
1021 };
1022 let mut read = Vec::with_capacity(sources);
1023 for _ in 0..sources {
1024 read.push(operands.source()?);
1025 }
1026 Item::Instruction(Instruction {
1027 word,
1028 opcode,
1029 control,
1030 coissue,
1031 predicate,
1032 destination,
1033 sources: read,
1034 })
1035 }
1036 };
1037 let used = operands.at.saturating_sub(at);
1038 if sm1 {
1039 if length != 0 && length != used {
1041 return Err(error(
1042 word,
1043 format!(
1044 "{} states {length} operand tokens, where its operands take {used}",
1045 opcode.name()
1046 ),
1047 ));
1048 }
1049 } else if used != length {
1050 return Err(error(
1051 word,
1052 format!(
1053 "{} states {length} operand tokens, where its operands take {used}",
1054 opcode.name()
1055 ),
1056 ));
1057 }
1058 self.at = operands.at;
1059 Ok(Some(item))
1060 }
1061}
1062
1063fn declared(token: u32, at: usize, register: Register) -> Result<Declared> {
1065 if token & 0x8000_0000 == 0 {
1066 return Err(error(at, format!("DCL token {token:#010x} lacks bit 31")));
1067 }
1068 if register.kind == RegisterType::Sampler {
1069 return Ok(Declared::Sampler(match token {
1070 0x9000_0000 => TextureType::D2,
1071 0x9800_0000 => TextureType::Cube,
1072 0xa000_0000 => TextureType::Volume,
1073 _ => {
1074 return Err(error(
1075 at,
1076 format!(
1077 "DCL sampler texture type {token:#x}, where it is 0x90000000 (2d), 0x98000000 (cube) or \
1078 0xa0000000 (volume)"
1079 ),
1080 ));
1081 }
1082 }));
1083 }
1084 if token & !0x800f_000f != 0 {
1085 return Err(error(at, "DCL semantic reserved bits"));
1086 }
1087 let usage = bits(token, 0, 15);
1088 if usage > 13 {
1089 return Err(error(at, format!("DCL usage {usage}, where it is 0 to 13")));
1090 }
1091 Ok(Declared::Semantic {
1092 usage,
1093 index: bits(token, 16, 15),
1094 })
1095}