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 unlocated: Vec::new(),
550 located: 0,
551 })
552}
553
554fn collect(mut records: Records<'_>) -> Result<Program> {
555 let mut items = Vec::new();
556 for item in records.by_ref() {
557 items.push(item?);
558 }
559 Ok(Program {
560 version: records.version,
561 items,
562 end: records.end.unwrap_or(0),
563 constants: records.constants,
564 debug: records.debug,
565 })
566}
567
568#[derive(Debug)]
570pub struct Records<'a> {
571 words: Words<'a>,
572 version: Version,
573 at: usize,
575 whole: bool,
577 end: Option<usize>,
578 done: bool,
579 constants: Option<ConstantTable>,
580 debug: Option<DebugInfo>,
581 unlocated: Vec<usize>,
584 located: usize,
586}
587
588impl Records<'_> {
589 pub fn version(&self) -> Version {
591 self.version
592 }
593 pub fn constants(&self) -> Option<&ConstantTable> {
595 self.constants.as_ref()
596 }
597 pub fn debug(&self) -> Option<&DebugInfo> {
599 self.debug.as_ref()
600 }
601 pub fn end(&self) -> Option<usize> {
603 self.end
604 }
605}
606
607impl Iterator for Records<'_> {
608 type Item = Result<Item>;
609
610 fn next(&mut self) -> Option<Result<Item>> {
611 if self.done {
612 return None;
613 }
614 let read = self.record().and_then(|item| {
615 match &item {
616 Some(Item::Comment(_)) => {}
617 Some(other) => self.unlocated.push(other.word()),
618 None => self.located_all()?,
619 }
620 self.locate()?;
621 Ok(item)
622 });
623 match read {
624 Ok(Some(item)) => Some(Ok(item)),
625 Ok(None) => {
626 self.done = true;
627 let words = self.words.len();
628 (self.whole && self.at != words).then(|| {
629 Err(error(
630 self.at,
631 format!("{} words after END", words.saturating_sub(self.at)),
632 ))
633 })
634 }
635 Err(e) => {
636 self.done = true;
637 Some(Err(e))
638 }
639 }
640 }
641}
642
643impl Records<'_> {
644 fn locate(&mut self) -> Result<()> {
649 let Some(debug) = &self.debug else {
650 return Ok(());
651 };
652 for word in std::mem::take(&mut self.unlocated) {
653 let n = self.located;
654 let offset = word.saturating_mul(4);
655 match debug.lines.get(n) {
656 Some(line) if usize::try_from(line.offset).is_ok_and(|o| o == offset) => {}
657 Some(line) => {
658 return Err(error(
659 word,
660 format!(
661 "DBUG line record {n} has offset {:#x}, where its record starts at {offset:#x}",
662 line.offset
663 ),
664 ));
665 }
666 None => {
667 return Err(error(
668 word,
669 format!(
670 "DBUG has {} line records, none for this record",
671 debug.lines.len()
672 ),
673 ));
674 }
675 }
676 self.located = n.saturating_add(1);
677 }
678 Ok(())
679 }
680
681 fn located_all(&mut self) -> Result<()> {
683 self.locate()?;
684 match &self.debug {
685 Some(debug) if debug.lines.len() != self.located => Err(error(
686 self.at.saturating_sub(1),
687 format!(
688 "DBUG has {} line records, where the program has {} declarations, definitions and instructions",
689 debug.lines.len(),
690 self.located
691 ),
692 )),
693 _ => Ok(()),
694 }
695 }
696}
697
698#[derive(Debug)]
700struct Words<'a> {
701 bytes: &'a [u8],
702}
703
704impl Words<'_> {
705 fn len(&self) -> usize {
706 (self.bytes.len() / 4).min(MAX_WORDS)
707 }
708 fn get(&self, at: usize) -> Option<u32> {
709 if at >= MAX_WORDS {
710 return None;
711 }
712 let start = at.checked_mul(4)?;
713 let word: [u8; 4] = self.bytes.get(start..start.checked_add(4)?)?.try_into().ok()?;
714 Some(u32::from_le_bytes(word))
715 }
716}
717
718fn bits(word: u32, shift: u32, mask: u32) -> u8 {
720 u8::try_from(word.checked_shr(shift).unwrap_or(0) & mask & 0xff).unwrap_or(u8::MAX)
721}
722
723fn register(token: u32, at: usize, version: Version) -> Result<Register> {
725 if token & 0x8000_0000 == 0 {
726 return Err(error(at, format!("operand token {token:#010x} lacks bit 31")));
727 }
728 let code = ((token >> 28) & 7) | ((token >> 8) & 0x18);
729 let number = token & 0x7ff;
730 let (kind, last) =
731 RegisterType::of(code, version).map_err(|why| error(at, format!("register type {code} {why}")))?;
732 if last.is_some_and(|last| number > last) {
733 return Err(error(
734 at,
735 format!(
736 "register {kind:?} {number}, where the type has registers 0 to {}",
737 last.unwrap_or(0)
738 ),
739 ));
740 }
741 Ok(Register { kind, number })
742}
743
744struct Operands<'a> {
747 words: &'a Words<'a>,
748 at: usize,
749 end: usize,
750 instruction: usize,
752 version: Version,
753}
754
755impl Operands<'_> {
756 fn next(&mut self) -> Result<(usize, u32)> {
757 let at = self.at;
758 let token = (at < self.end)
759 .then(|| self.words.get(at))
760 .flatten()
761 .ok_or_else(|| {
762 error(
763 self.instruction,
764 format!(
765 "instruction's operand span extends past {} (it ends at word {})",
766 if self.end < self.words.len() {
767 "its stated length"
768 } else {
769 "the stream"
770 },
771 self.end.min(self.words.len())
772 ),
773 )
774 })?;
775 self.at = at.saturating_add(1);
776 Ok((at, token))
777 }
778
779 fn relative(&mut self, operand: usize, what: &str) -> Result<Relative> {
781 let version = self.version;
782 if version.major == 1 {
783 return Ok(Relative {
784 word: None,
785 register: Register {
786 kind: RegisterType::Address,
787 number: 0,
788 },
789 swizzle: [0; 4],
790 });
791 }
792 let (at, token) = self.next().map_err(|e| {
793 error(
794 operand,
795 format!(
796 "relative addressing of {what} without its address token: {}",
797 e.detail
798 ),
799 )
800 })?;
801 let register = register(token & !0x2000, at, version)?;
802 if !matches!(
804 register.kind,
805 RegisterType::Address | RegisterType::Texture | RegisterType::Loop
806 ) || token & 0x2000 != 0
807 {
808 return Err(error(
809 at,
810 format!(
811 "relative address token of {what} names {register:?}{}, where it is the address register a0 or the \
812 loop counter aL",
813 if token & 0x2000 != 0 {
814 " relatively addressed itself"
815 } else {
816 ""
817 }
818 ),
819 ));
820 }
821 if (token >> 24) & 15 != 0 {
822 return Err(error(
823 at,
824 format!(
825 "relative address token of {what} has source modifier {}, where it has none",
826 (token >> 24) & 15
827 ),
828 ));
829 }
830 Ok(Relative {
831 word: Some(at),
832 register,
833 swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
834 })
835 }
836
837 fn destination(&mut self) -> Result<Destination> {
838 let (at, token) = self.next()?;
839 let version = self.version;
840 let register = register(token & !0x2000, at, version)?;
841 let modifiers = (token >> 20) & 15;
842 if modifiers & 8 != 0 {
843 return Err(error(
844 at,
845 format!("destination {token:#010x} sets result modifier bit 23"),
846 ));
847 }
848 let shift = bits(token, 24, 15);
849 let shift = if shift >= 8 {
850 i8::try_from(i16::from(shift).saturating_sub(16)).unwrap_or(0)
851 } else {
852 i8::try_from(shift).unwrap_or(0)
853 };
854 if !(-3..=3).contains(&shift) {
856 return Err(error(
857 at,
858 format!("destination {token:#010x} has result shift {shift}, where it is -3 to 3"),
859 ));
860 }
861 let relative = if token & 0x2000 != 0 {
863 Some(self.relative(at, "a destination")?)
864 } else {
865 None
866 };
867 Ok(Destination {
868 word: at,
869 register,
870 mask: bits(token, 16, 15),
871 saturate: modifiers & 1 != 0,
872 partial_precision: modifiers & 2 != 0,
873 centroid: modifiers & 4 != 0,
874 shift,
875 relative,
876 precision: Precision::of(token),
877 })
878 }
879
880 fn source(&mut self) -> Result<Source> {
881 let (at, token) = self.next()?;
882 let version = self.version;
883 let register = register(token & !0x2000, at, version)?;
884 let modifier = Modifier::of((token >> 24) & 15).ok_or_else(|| {
885 error(
886 at,
887 format!("source modifier {} is not one of 0 to 13", (token >> 24) & 15),
888 )
889 })?;
890 let relative = if token & 0x2000 != 0 {
891 Some(self.relative(at, "a source")?)
892 } else {
893 None
894 };
895 Ok(Source {
896 word: at,
897 register,
898 swizzle: [16, 18, 20, 22].map(|shift| bits(token, shift, 3)),
899 modifier,
900 relative,
901 precision: Precision::of(token),
902 })
903 }
904}
905
906impl Records<'_> {
907 fn record(&mut self) -> Result<Option<Item>> {
909 let version = self.version;
910 let words = &self.words;
911 let mut at = self.at;
912 let word = at;
913 let token = words.get(word).ok_or_else(|| {
914 error(
915 word,
916 if word >= MAX_WORDS {
917 "the program exceeds 1,048,576 words without END"
918 } else {
919 "missing END"
920 },
921 )
922 })?;
923 at = at.saturating_add(1);
924 if token == 0xffff {
925 self.end = Some(word);
926 self.at = at;
927 return Ok(None);
928 }
929 let op = token & 0xffff;
930 if op == 0xfffe {
931 if token & 0x8000_0000 != 0 {
932 return Err(error(word, "COMMENT reserved bit"));
933 }
934 let count = usize::try_from((token >> 16) & 0x7fff).unwrap_or(usize::MAX);
935 let end = at.saturating_add(count);
936 let data: Vec<u32> = (at..end).map_while(|n| words.get(n)).collect();
937 if data.len() != count {
938 return Err(error(
939 word,
940 format!(
941 "instruction/comment span of {count} words extends past the stream's {} words",
942 words.len()
943 ),
944 ));
945 }
946 if let Some((&tag, rest)) = data.split_first() {
947 let bytes = || -> Vec<u8> { rest.iter().flat_map(|w| w.to_le_bytes()).collect() };
948 if tag == u32::from_le_bytes(*b"CTAB") {
949 if self.constants.is_some() {
950 return Err(error(word, "duplicate CTAB"));
951 }
952 self.constants = Some(crate::ctab::read(
953 &bytes(),
954 word.saturating_add(2),
955 version.token(),
956 )?);
957 } else if tag == u32::from_le_bytes(*b"DBUG") {
958 if self.debug.is_some() {
959 return Err(error(word, "duplicate DBUG"));
960 }
961 self.debug = Some(crate::debug::read(&bytes(), word.saturating_add(2))?);
962 }
963 }
964 self.at = end;
965 return Ok(Some(Item::Comment(Comment { word, data })));
966 }
967 let opcode = u16::try_from(op)
968 .ok()
969 .and_then(Opcode::from_code)
970 .ok_or_else(|| error(word, format!("opcode {op} is not a Direct3D 9 instruction")))?;
971 let sm1 = version.major == 1;
973 let length = usize::from(bits(token, 24, 15));
974 let shape = match (opcode, opcode.shape(version)) {
975 (Opcode::Sgn, Shape::Operands { destination, .. }) if version.major == 3 && length == 4 => {
978 Shape::Operands {
979 destination,
980 sources: 3,
981 }
982 }
983 (_, shape) => shape,
984 };
985 let predicated = token & 0x1000_0000 != 0;
986 let coissue = token & 0x4000_0000 != 0;
987 let control = bits(token, 16, 0xff);
988 if token & 0xa000_0000 != 0 {
989 return Err(error(
990 word,
991 format!("instruction token {token:#010x} sets reserved bit 29 or 31"),
992 ));
993 }
994 if shape == Shape::Phase && token != 0xfffd {
997 return Err(error(
998 word,
999 format!("phase token {token:#010x} has bits beyond the opcode"),
1000 ));
1001 }
1002 let controlled = match opcode {
1003 Opcode::Tex if !sm1 => (0..=2).contains(&control),
1004 Opcode::IfC | Opcode::BreakC | Opcode::SetP => (1..=6).contains(&control),
1005 _ => control == 0,
1006 };
1007 if !controlled {
1008 return Err(error(
1009 word,
1010 format!(
1011 "{} has control bits {control:#x}, which it does not define",
1012 opcode.name()
1013 ),
1014 ));
1015 }
1016 let end = if sm1 {
1017 words.len()
1018 } else {
1019 let end = at.saturating_add(length);
1020 if end > words.len() {
1021 return Err(error(
1022 word,
1023 format!(
1024 "instruction/comment span of {length} operands extends past the stream's {} words",
1025 words.len()
1026 ),
1027 ));
1028 }
1029 end
1030 };
1031 let mut operands = Operands {
1032 words,
1033 at,
1034 end,
1035 instruction: word,
1036 version,
1037 };
1038 let item = match shape {
1039 Shape::Phase => Item::Instruction(Instruction {
1040 word,
1041 opcode,
1042 control,
1043 coissue,
1044 predicate: None,
1045 destination: None,
1046 sources: Vec::new(),
1047 }),
1048 Shape::Declaration => {
1049 if predicated {
1050 return Err(error(word, "predicated dcl"));
1051 }
1052 let (token_at, token) = operands.next()?;
1053 let destination = operands.destination()?;
1054 Item::Declaration(Declaration {
1055 word,
1056 token,
1057 declared: declared(token, token_at, destination.register)?,
1058 destination,
1059 })
1060 }
1061 Shape::Definition { words: count } => {
1062 if predicated {
1063 return Err(error(word, format!("predicated {}", opcode.name())));
1064 }
1065 let destination = operands.destination()?;
1066 let expected = match opcode {
1067 Opcode::Def => RegisterType::Const,
1068 Opcode::DefI => RegisterType::ConstInt,
1069 _ => RegisterType::ConstBool,
1070 };
1071 if destination.register.kind != expected || destination.relative.is_some() {
1072 return Err(error(
1073 destination.word,
1074 format!(
1075 "{} defines {:?}, where it defines a {expected:?} register",
1076 opcode.name(),
1077 destination.register
1078 ),
1079 ));
1080 }
1081 let mut values = [0u32; 4];
1082 for value in values.iter_mut().take(count) {
1083 *value = operands.next()?.1;
1084 }
1085 let literal = match opcode {
1086 Opcode::Def => Literal::Float(values),
1087 Opcode::DefI => Literal::Integer(values.map(|v| i32::from_le_bytes(v.to_le_bytes()))),
1088 _ => {
1089 let [value, ..] = values;
1090 Literal::Bool(value)
1091 }
1092 };
1093 Item::Definition(Definition {
1094 word,
1095 destination,
1096 literal,
1097 })
1098 }
1099 Shape::Operands { destination, sources } => {
1100 let destination = if destination {
1101 Some(operands.destination()?)
1102 } else {
1103 None
1104 };
1105 let predicate = if predicated {
1106 let p = operands.source()?;
1107 if p.register.kind != RegisterType::Predicate
1108 || !matches!(p.modifier, Modifier::None | Modifier::Not)
1109 {
1110 return Err(error(
1111 p.word,
1112 format!(
1113 "the predicate token names {:?} with {:?}, where it is p0 or !p0",
1114 p.register, p.modifier
1115 ),
1116 ));
1117 }
1118 Some(p)
1119 } else {
1120 None
1121 };
1122 let mut read = Vec::with_capacity(sources);
1123 for _ in 0..sources {
1124 read.push(operands.source()?);
1125 }
1126 Item::Instruction(Instruction {
1127 word,
1128 opcode,
1129 control,
1130 coissue,
1131 predicate,
1132 destination,
1133 sources: read,
1134 })
1135 }
1136 };
1137 let used = operands.at.saturating_sub(at);
1138 if sm1 {
1139 if length != 0 && length != used {
1141 return Err(error(
1142 word,
1143 format!(
1144 "{} states {length} operand tokens, where its operands take {used}",
1145 opcode.name()
1146 ),
1147 ));
1148 }
1149 } else if used != length {
1150 return Err(error(
1151 word,
1152 format!(
1153 "{} states {length} operand tokens, where its operands take {used}",
1154 opcode.name()
1155 ),
1156 ));
1157 }
1158 self.at = operands.at;
1159 Ok(Some(item))
1160 }
1161}
1162
1163fn declared(token: u32, at: usize, register: Register) -> Result<Declared> {
1165 if token & 0x8000_0000 == 0 {
1166 return Err(error(at, format!("DCL token {token:#010x} lacks bit 31")));
1167 }
1168 if register.kind == RegisterType::Sampler {
1169 return Ok(Declared::Sampler(match token {
1170 0x9000_0000 => TextureType::D2,
1171 0x9800_0000 => TextureType::Cube,
1172 0xa000_0000 => TextureType::Volume,
1173 _ => {
1174 return Err(error(
1175 at,
1176 format!(
1177 "DCL sampler texture type {token:#x}, where it is 0x90000000 (2d), 0x98000000 (cube) or \
1178 0xa0000000 (volume)"
1179 ),
1180 ));
1181 }
1182 }));
1183 }
1184 if token & !0x800f_000f != 0 {
1185 return Err(error(at, "DCL semantic reserved bits"));
1186 }
1187 let usage = bits(token, 0, 15);
1188 if usage > 13 {
1189 return Err(error(at, format!("DCL usage {usage}, where it is 0 to 13")));
1190 }
1191 Ok(Declared::Semantic {
1192 usage,
1193 index: bits(token, 16, 15),
1194 })
1195}