1use crate::{Stage, Version};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub enum Opcode {
11 Nop,
13 Mov,
15 Add,
17 Sub,
19 Mad,
21 Mul,
23 Rcp,
25 Rsq,
27 Dp3,
29 Dp4,
31 Min,
33 Max,
35 Slt,
37 Sge,
39 Exp,
41 Log,
43 Lit,
45 Dst,
47 Lrp,
49 Frc,
51 M4x4,
53 M4x3,
55 M3x4,
57 M3x3,
59 M3x2,
61 Call,
63 CallNz,
65 Loop,
67 Ret,
69 EndLoop,
71 Label,
73 Dcl,
75 Pow,
77 Crs,
79 Sgn,
81 Abs,
83 Nrm,
85 SinCos,
87 Rep,
89 EndRep,
91 If,
93 IfC,
95 Else,
97 EndIf,
99 Break,
101 BreakC,
103 Mova,
105 DefB,
107 DefI,
109 TexCoord,
111 TexKill,
113 Tex,
115 TexBem,
117 TexBemL,
119 TexReg2Ar,
121 TexReg2Gb,
123 TexM3x2Pad,
125 TexM3x2Tex,
127 TexM3x3Pad,
129 TexM3x3Tex,
131 TexM3x3Spec,
133 TexM3x3VSpec,
135 ExpP,
137 LogP,
139 Cnd,
141 Def,
143 TexReg2Rgb,
145 TexDp3Tex,
147 TexM3x2Depth,
149 TexDp3,
151 TexM3x3,
153 TexDepth,
155 Cmp,
157 Bem,
159 Dp2Add,
161 Dsx,
163 Dsy,
165 TexLdd,
167 SetP,
170 TexLdl,
172 BreakP,
174 Phase,
176}
177
178const TABLE: &[(Opcode, u16, &str)] = &[
180 (Opcode::Nop, 0, "nop"),
181 (Opcode::Mov, 1, "mov"),
182 (Opcode::Add, 2, "add"),
183 (Opcode::Sub, 3, "sub"),
184 (Opcode::Mad, 4, "mad"),
185 (Opcode::Mul, 5, "mul"),
186 (Opcode::Rcp, 6, "rcp"),
187 (Opcode::Rsq, 7, "rsq"),
188 (Opcode::Dp3, 8, "dp3"),
189 (Opcode::Dp4, 9, "dp4"),
190 (Opcode::Min, 10, "min"),
191 (Opcode::Max, 11, "max"),
192 (Opcode::Slt, 12, "slt"),
193 (Opcode::Sge, 13, "sge"),
194 (Opcode::Exp, 14, "exp"),
195 (Opcode::Log, 15, "log"),
196 (Opcode::Lit, 16, "lit"),
197 (Opcode::Dst, 17, "dst"),
198 (Opcode::Lrp, 18, "lrp"),
199 (Opcode::Frc, 19, "frc"),
200 (Opcode::M4x4, 20, "m4x4"),
201 (Opcode::M4x3, 21, "m4x3"),
202 (Opcode::M3x4, 22, "m3x4"),
203 (Opcode::M3x3, 23, "m3x3"),
204 (Opcode::M3x2, 24, "m3x2"),
205 (Opcode::Call, 25, "call"),
206 (Opcode::CallNz, 26, "callnz"),
207 (Opcode::Loop, 27, "loop"),
208 (Opcode::Ret, 28, "ret"),
209 (Opcode::EndLoop, 29, "endloop"),
210 (Opcode::Label, 30, "label"),
211 (Opcode::Dcl, 31, "dcl"),
212 (Opcode::Pow, 32, "pow"),
213 (Opcode::Crs, 33, "crs"),
214 (Opcode::Sgn, 34, "sgn"),
215 (Opcode::Abs, 35, "abs"),
216 (Opcode::Nrm, 36, "nrm"),
217 (Opcode::SinCos, 37, "sincos"),
218 (Opcode::Rep, 38, "rep"),
219 (Opcode::EndRep, 39, "endrep"),
220 (Opcode::If, 40, "if"),
221 (Opcode::IfC, 41, "if_comp"),
222 (Opcode::Else, 42, "else"),
223 (Opcode::EndIf, 43, "endif"),
224 (Opcode::Break, 44, "break"),
225 (Opcode::BreakC, 45, "break_comp"),
226 (Opcode::Mova, 46, "mova"),
227 (Opcode::DefB, 47, "defb"),
228 (Opcode::DefI, 48, "defi"),
229 (Opcode::TexCoord, 64, "texcoord"),
230 (Opcode::TexKill, 65, "texkill"),
231 (Opcode::Tex, 66, "tex"),
232 (Opcode::TexBem, 67, "texbem"),
233 (Opcode::TexBemL, 68, "texbeml"),
234 (Opcode::TexReg2Ar, 69, "texreg2ar"),
235 (Opcode::TexReg2Gb, 70, "texreg2gb"),
236 (Opcode::TexM3x2Pad, 71, "texm3x2pad"),
237 (Opcode::TexM3x2Tex, 72, "texm3x2tex"),
238 (Opcode::TexM3x3Pad, 73, "texm3x3pad"),
239 (Opcode::TexM3x3Tex, 74, "texm3x3tex"),
240 (Opcode::TexM3x3Spec, 76, "texm3x3spec"),
241 (Opcode::TexM3x3VSpec, 77, "texm3x3vspec"),
242 (Opcode::ExpP, 78, "expp"),
243 (Opcode::LogP, 79, "logp"),
244 (Opcode::Cnd, 80, "cnd"),
245 (Opcode::Def, 81, "def"),
246 (Opcode::TexReg2Rgb, 82, "texreg2rgb"),
247 (Opcode::TexDp3Tex, 83, "texdp3tex"),
248 (Opcode::TexM3x2Depth, 84, "texm3x2depth"),
249 (Opcode::TexDp3, 85, "texdp3"),
250 (Opcode::TexM3x3, 86, "texm3x3"),
251 (Opcode::TexDepth, 87, "texdepth"),
252 (Opcode::Cmp, 88, "cmp"),
253 (Opcode::Bem, 89, "bem"),
254 (Opcode::Dp2Add, 90, "dp2add"),
255 (Opcode::Dsx, 91, "dsx"),
256 (Opcode::Dsy, 92, "dsy"),
257 (Opcode::TexLdd, 93, "texldd"),
258 (Opcode::SetP, 94, "setp_comp"),
259 (Opcode::TexLdl, 95, "texldl"),
260 (Opcode::BreakP, 96, "breakp"),
261 (Opcode::Phase, 0xfffd, "phase"),
262];
263
264#[derive(Clone, Copy, Debug, PartialEq, Eq)]
266pub(crate) enum Shape {
267 Operands { destination: bool, sources: usize },
270 Declaration,
272 Definition { words: usize },
274 Phase,
276}
277
278type Range = Option<((u8, u8), (u8, u8))>;
280
281const ALL: Range = Some(((1, 0), (3, 255)));
282const FROM_2: Range = Some(((2, 0), (3, 255)));
283const FROM_2X: Range = Some(((2, 1), (3, 255)));
284const FROM_3: Range = Some(((3, 0), (3, 255)));
285const NONE: Range = None;
286const fn pixel(from: (u8, u8), to: (u8, u8)) -> Range {
287 Some((from, to))
288}
289
290impl Opcode {
291 pub fn code(self) -> u16 {
293 TABLE
294 .iter()
295 .find(|row| row.0 == self)
296 .map_or(u16::MAX, |row| row.1)
297 }
298
299 pub fn name(self) -> &'static str {
303 TABLE.iter().find(|row| row.0 == self).map_or("", |row| row.2)
304 }
305
306 pub fn from_code(code: u16) -> Option<Self> {
309 TABLE.iter().find(|row| row.1 == code).map(|row| row.0)
310 }
311
312 fn versions(self) -> (Range, Range) {
314 use Opcode::*;
315 match self {
316 Nop | Mov | Add | Sub | Mad | Mul | Dp3 | Def => (ALL, ALL),
317 Dcl => (ALL, FROM_2),
318 Rcp | Rsq | Min | Max | Exp | Log | Frc | M4x4 | M4x3 | M3x4 | M3x3 | M3x2 => (ALL, FROM_2),
319 Dp4 => (ALL, pixel((1, 2), (3, 255))),
320 Slt | Sge | Lit | Dst | ExpP | LogP => (ALL, NONE),
321 Lrp => (FROM_2, ALL),
322 Pow | Crs | Abs | Nrm | SinCos => (FROM_2, FROM_2),
323 Sgn | Mova => (FROM_2, NONE),
324 Call | CallNz | Ret | Label | Rep | EndRep | If | Else | EndIf | DefB | DefI => (FROM_2, FROM_2X),
325 Loop | EndLoop => (FROM_2, FROM_3),
326 IfC | Break | BreakC | SetP | BreakP => (FROM_2X, FROM_2X),
327 TexLdl => (FROM_3, FROM_3),
328 TexKill | Tex => (NONE, ALL),
329 TexCoord => (NONE, pixel((1, 0), (1, 4))),
330 TexBem | TexBemL | TexReg2Ar | TexReg2Gb | TexM3x2Pad | TexM3x2Tex | TexM3x3Pad | TexM3x3Tex
331 | TexM3x3Spec | TexM3x3VSpec => (NONE, pixel((1, 0), (1, 3))),
332 TexReg2Rgb | TexDp3Tex | TexDp3 | TexM3x3 => (NONE, pixel((1, 2), (1, 3))),
333 TexM3x2Depth => (NONE, pixel((1, 3), (1, 3))),
334 Cnd => (NONE, pixel((1, 0), (1, 4))),
335 Cmp => (NONE, pixel((1, 2), (3, 255))),
336 TexDepth | Bem | Phase => (NONE, pixel((1, 4), (1, 4))),
337 Dp2Add => (NONE, FROM_2),
338 Dsx | Dsy | TexLdd => (NONE, FROM_2X),
339 }
340 }
341
342 pub fn in_version(self, version: Version) -> bool {
345 let (vertex, pixel) = self.versions();
346 let range = match version.stage {
347 Stage::Vertex => vertex,
348 Stage::Pixel => pixel,
349 };
350 range.is_some_and(|(from, to)| (from..=to).contains(&(version.major, version.minor)))
351 }
352
353 pub(crate) fn shape(self, version: Version) -> Shape {
355 use Opcode::*;
356 let sm1 = version.major == 1;
357 let sm3 = version.major == 3;
358 let (destination, sources) = match self {
359 Dcl => return Shape::Declaration,
360 Def | DefI => return Shape::Definition { words: 4 },
361 DefB => return Shape::Definition { words: 1 },
362 Phase => return Shape::Phase,
363 Nop | Ret | EndLoop | EndRep | Else | EndIf | Break => (false, 0),
364 Call | Label | Rep | If | BreakP => (false, 1),
365 CallNz | Loop | IfC | BreakC => (false, 2),
366 TexKill | TexDepth => (true, 0),
367 Tex if sm1 => (true, usize::from(version.minor == 4)),
370 Tex => (true, 2),
371 TexCoord => (true, usize::from(version.minor == 4)),
372 Sgn | SinCos if !sm3 => (true, 3),
373 Mov | Rcp | Rsq | Exp | Log | Lit | Frc | Abs | Nrm | Sgn | SinCos | Mova | ExpP | LogP | Dsx
374 | Dsy | TexBem | TexBemL | TexReg2Ar | TexReg2Gb | TexM3x2Pad | TexM3x2Tex | TexM3x3Pad
375 | TexM3x3Tex | TexM3x3VSpec | TexReg2Rgb | TexDp3Tex | TexM3x2Depth | TexDp3 | TexM3x3 => {
376 (true, 1)
377 }
378 Add | Sub | Mul | Dp3 | Dp4 | Min | Max | Slt | Sge | Dst | M4x4 | M4x3 | M3x4 | M3x3 | M3x2
379 | Pow | Crs | TexM3x3Spec | Bem | SetP | TexLdl => (true, 2),
380 Mad | Lrp | Cnd | Cmp | Dp2Add => (true, 3),
381 TexLdd => (true, 4),
382 };
383 Shape::Operands { destination, sources }
384 }
385}