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 {
344 let (vertex, pixel) = self.versions();
345 let range = match version.stage {
346 Stage::Vertex => vertex,
347 Stage::Pixel => pixel,
348 };
349 range.is_some_and(|(from, to)| (from..=to).contains(&(version.major, version.minor)))
350 }
351
352 pub(crate) fn shape(self, version: Version) -> Shape {
354 use Opcode::*;
355 let sm1 = version.major == 1;
356 let sm3 = version.major == 3;
357 let (destination, sources) = match self {
358 Dcl => return Shape::Declaration,
359 Def | DefI => return Shape::Definition { words: 4 },
360 DefB => return Shape::Definition { words: 1 },
361 Phase => return Shape::Phase,
362 Nop | Ret | EndLoop | EndRep | Else | EndIf | Break => (false, 0),
363 Call | Label | Rep | If | BreakP => (false, 1),
364 CallNz | Loop | IfC | BreakC => (false, 2),
365 TexKill | TexDepth => (true, 0),
366 Tex if sm1 => (true, usize::from(version.minor == 4)),
369 Tex => (true, 2),
370 TexCoord => (true, usize::from(version.minor == 4)),
371 Sgn | SinCos if !sm3 => (true, 3),
372 Mov | Rcp | Rsq | Exp | Log | Lit | Frc | Abs | Nrm | Sgn | SinCos | Mova | ExpP | LogP | Dsx
373 | Dsy | TexBem | TexBemL | TexReg2Ar | TexReg2Gb | TexM3x2Pad | TexM3x2Tex | TexM3x3Pad
374 | TexM3x3Tex | TexM3x3VSpec | TexReg2Rgb | TexDp3Tex | TexM3x2Depth | TexDp3 | TexM3x3 => {
375 (true, 1)
376 }
377 Add | Sub | Mul | Dp3 | Dp4 | Min | Max | Slt | Sge | Dst | M4x4 | M4x3 | M3x4 | M3x3 | M3x2
378 | Pow | Crs | TexM3x3Spec | Bem | SetP | TexLdl => (true, 2),
379 Mad | Lrp | Cnd | Cmp | Dp2Add => (true, 3),
380 TexLdd => (true, 4),
381 };
382 Shape::Operands { destination, sources }
383 }
384}