1#![allow(clippy::needless_range_loop)]
32
33use core::cmp;
34
35#[derive(Copy, Clone)]
39struct Cmd {
40 sf: bool,
43 lm: bool,
47 mx: u8,
51 vx: u8,
54 cv: u8,
57}
58
59impl Cmd {
60 fn decode(instr: u32) -> Self {
61 Self {
62 sf: (instr >> 19) & 1 != 0,
63 lm: (instr >> 10) & 1 != 0,
64 mx: ((instr >> 17) & 0b11) as u8,
65 vx: ((instr >> 15) & 0b11) as u8,
66 cv: ((instr >> 13) & 0b11) as u8,
67 }
68 }
69
70 fn shift(&self) -> u32 {
71 if self.sf {
72 12
73 } else {
74 0
75 }
76 }
77}
78
79mod flag {
81 pub const IR0_SAT: u32 = 1 << 12;
82 pub const SY2_SAT: u32 = 1 << 13;
83 pub const SX2_SAT: u32 = 1 << 14;
84 pub const MAC0_NEG: u32 = 1 << 15;
85 pub const MAC0_POS: u32 = 1 << 16;
86 pub const DIV_OVERFLOW: u32 = 1 << 17;
87 pub const SZ3_OTZ_SAT: u32 = 1 << 18;
88 pub const COLOR_B_SAT: u32 = 1 << 19;
89 pub const COLOR_G_SAT: u32 = 1 << 20;
90 pub const COLOR_R_SAT: u32 = 1 << 21;
91 pub const IR3_SAT: u32 = 1 << 22;
92 pub const IR2_SAT: u32 = 1 << 23;
93 pub const IR1_SAT: u32 = 1 << 24;
94 pub const MAC3_NEG: u32 = 1 << 25;
95 pub const MAC2_NEG: u32 = 1 << 26;
96 pub const MAC1_NEG: u32 = 1 << 27;
97 pub const MAC3_POS: u32 = 1 << 28;
98 pub const MAC2_POS: u32 = 1 << 29;
99 pub const MAC1_POS: u32 = 1 << 30;
100 pub const ERROR_MASK: u32 = 0x7F87_E000;
102}
103
104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109pub struct Gte {
110 v: [[i16; 3]; 3],
113 rgbc: [u8; 4],
116 otz: u16,
118 ir0: i16,
121 ir: [i16; 3],
124 sxy: [[i16; 2]; 3],
128 sz: [u16; 4],
131 rgb_fifo: [[u8; 4]; 3],
134 res1: u32,
137 mac0: i32,
139 mac: [i32; 3],
142 lzcs: u32,
150 lzcr: u32,
151
152 #[cfg_attr(feature = "serde", serde(skip))]
167 stale_v0x: Option<i16>,
168 #[cfg_attr(feature = "serde", serde(skip))]
172 stale_op_ir3: Option<i16>,
173
174 rotation: [[i16; 3]; 3],
177 translation: [i32; 3],
179 light: [[i16; 3]; 3],
181 bg_color: [i32; 3],
185 light_color: [[i16; 3]; 3],
187 far_color: [i32; 3],
189 ofx: i32,
191 ofy: i32,
193 h: u16,
196 dqa: i16,
198 dqb: i32,
200 zsf3: i16,
202 zsf4: i16,
203 flag: u32,
205 profile_ops: u64,
209 profile_estimated_cycles: u64,
214 #[cfg_attr(feature = "serde", serde(skip, default = "default_opcode_counts"))]
218 profile_opcode_counts: [u64; 64],
219}
220
221#[cfg(feature = "serde")]
226fn default_opcode_counts() -> [u64; 64] {
227 [0; 64]
228}
229
230#[derive(Clone, Copy, Debug, PartialEq, Eq)]
232pub struct GteProfileSnapshot {
233 pub ops: u64,
235 pub estimated_cycles: u64,
237 pub opcode_counts: [u64; 64],
239}
240
241fn gte_command_cycles(opcode: u8) -> Option<u8> {
245 match opcode {
246 0x01 => Some(15), 0x06 => Some(8), 0x0C => Some(6), 0x10 => Some(8), 0x11 => Some(8), 0x12 => Some(8), 0x13 => Some(19), 0x14 => Some(13), 0x16 => Some(44), 0x1B => Some(17), 0x1C => Some(11), 0x1E => Some(14), 0x20 => Some(30), 0x28 => Some(5), 0x29 => Some(8), 0x2A => Some(17), 0x2D => Some(5), 0x2E => Some(6), 0x30 => Some(23), 0x3D => Some(5), 0x3E => Some(5), 0x3F => Some(39), _ => None,
269 }
270}
271
272impl Gte {
273 pub fn command_cycles(instr: u32) -> u32 {
278 gte_command_cycles((instr & 0x3F) as u8).map_or(0, u32::from)
279 }
280
281 pub fn new() -> Self {
286 Self {
287 v: [[0; 3]; 3],
288 rgbc: [0; 4],
289 otz: 0,
290 ir0: 0,
291 ir: [0; 3],
292 sxy: [[0; 2]; 3],
293 sz: [0; 4],
294 rgb_fifo: [[0; 4]; 3],
295 res1: 0,
296 mac0: 0,
297 mac: [0; 3],
298 lzcs: 0,
299 lzcr: 0,
300 stale_v0x: None,
301 stale_op_ir3: None,
302 rotation: [[0; 3]; 3],
303 translation: [0; 3],
304 light: [[0; 3]; 3],
305 bg_color: [0; 3],
306 light_color: [[0; 3]; 3],
307 far_color: [0; 3],
308 ofx: 0,
309 ofy: 0,
310 h: 0,
311 dqa: 0,
312 dqb: 0,
313 zsf3: 0,
314 zsf4: 0,
315 flag: 0,
316 profile_ops: 0,
317 profile_estimated_cycles: 0,
318 profile_opcode_counts: [0; 64],
319 }
320 }
321
322 pub fn profile_snapshot(&self) -> GteProfileSnapshot {
324 GteProfileSnapshot {
325 ops: self.profile_ops,
326 estimated_cycles: self.profile_estimated_cycles,
327 opcode_counts: self.profile_opcode_counts,
328 }
329 }
330
331 pub fn read_data(&self, idx: u8) -> u32 {
334 match idx & 31 {
335 0 => pack_xy_i16(self.v[0][0], self.v[0][1]),
336 1 => sign_extend_16(self.v[0][2]),
337 2 => pack_xy_i16(self.v[1][0], self.v[1][1]),
338 3 => sign_extend_16(self.v[1][2]),
339 4 => pack_xy_i16(self.v[2][0], self.v[2][1]),
340 5 => sign_extend_16(self.v[2][2]),
341 6 => u32::from_le_bytes(self.rgbc),
342 7 => self.otz as u32,
343 8 => sign_extend_16(self.ir0),
344 9 => sign_extend_16(self.ir[0]),
345 10 => sign_extend_16(self.ir[1]),
346 11 => sign_extend_16(self.ir[2]),
347 12 => pack_xy_i16(self.sxy[0][0], self.sxy[0][1]),
348 13 => pack_xy_i16(self.sxy[1][0], self.sxy[1][1]),
349 14 | 15 => pack_xy_i16(self.sxy[2][0], self.sxy[2][1]),
352 16 => self.sz[0] as u32,
353 17 => self.sz[1] as u32,
354 18 => self.sz[2] as u32,
355 19 => self.sz[3] as u32,
356 20 => u32::from_le_bytes(self.rgb_fifo[0]),
357 21 => u32::from_le_bytes(self.rgb_fifo[1]),
358 22 => u32::from_le_bytes(self.rgb_fifo[2]),
359 23 => self.res1,
360 24 => self.mac0 as u32,
361 25 => self.mac[0] as u32,
362 26 => self.mac[1] as u32,
363 27 => self.mac[2] as u32,
364 28 | 29 => pack_irgb(&self.ir),
367 30 => self.lzcs,
368 31 => self.lzcr,
369 _ => unreachable!(),
370 }
371 }
372
373 pub fn write_data(&mut self, idx: u8, value: u32) {
375 match idx & 31 {
376 0 => {
377 let (x, y) = unpack_xy_i16(value);
378 self.v[0][0] = x;
379 self.v[0][1] = y;
380 }
381 1 => self.v[0][2] = value as i16,
382 2 => {
383 let (x, y) = unpack_xy_i16(value);
384 self.v[1][0] = x;
385 self.v[1][1] = y;
386 }
387 3 => self.v[1][2] = value as i16,
388 4 => {
389 let (x, y) = unpack_xy_i16(value);
390 self.v[2][0] = x;
391 self.v[2][1] = y;
392 }
393 5 => self.v[2][2] = value as i16,
394 6 => self.rgbc = value.to_le_bytes(),
395 7 => self.otz = value as u16,
396 8 => self.ir0 = value as i16,
397 9 => self.ir[0] = value as i16,
398 10 => self.ir[1] = value as i16,
399 11 => self.ir[2] = value as i16,
400 12 => {
401 let (x, y) = unpack_xy_i16(value);
402 self.sxy[0] = [x, y];
403 }
404 13 => {
405 let (x, y) = unpack_xy_i16(value);
406 self.sxy[1] = [x, y];
407 }
408 14 => {
409 let (x, y) = unpack_xy_i16(value);
410 self.sxy[2] = [x, y];
411 }
412 15 => {
414 let (x, y) = unpack_xy_i16(value);
415 self.sxy[0] = self.sxy[1];
416 self.sxy[1] = self.sxy[2];
417 self.sxy[2] = [x, y];
418 }
419 16 => self.sz[0] = value as u16,
420 17 => self.sz[1] = value as u16,
421 18 => self.sz[2] = value as u16,
422 19 => self.sz[3] = value as u16,
423 20 => self.rgb_fifo[0] = value.to_le_bytes(),
424 21 => self.rgb_fifo[1] = value.to_le_bytes(),
425 22 => self.rgb_fifo[2] = value.to_le_bytes(),
426 23 => self.res1 = value,
427 24 => self.mac0 = value as i32,
428 25 => self.mac[0] = value as i32,
429 26 => self.mac[1] = value as i32,
430 27 => self.mac[2] = value as i32,
431 28 => {
434 let r = (value & 0x1F) as i16;
435 let g = ((value >> 5) & 0x1F) as i16;
436 let b = ((value >> 10) & 0x1F) as i16;
437 self.ir[0] = r << 7;
438 self.ir[1] = g << 7;
439 self.ir[2] = b << 7;
440 }
441 29 => {}
443 30 => {
444 self.lzcs = value;
445 self.lzcr = leading_count_signed(value);
446 }
447 31 => {}
449 _ => unreachable!(),
450 }
451 }
452
453 pub fn read_control(&self, idx: u8) -> u32 {
455 match idx & 31 {
456 0 => pack_xy_i16(self.rotation[0][0], self.rotation[0][1]),
457 1 => pack_xy_i16(self.rotation[0][2], self.rotation[1][0]),
458 2 => pack_xy_i16(self.rotation[1][1], self.rotation[1][2]),
459 3 => pack_xy_i16(self.rotation[2][0], self.rotation[2][1]),
460 4 => sign_extend_16(self.rotation[2][2]),
462 5 => self.translation[0] as u32,
463 6 => self.translation[1] as u32,
464 7 => self.translation[2] as u32,
465 8 => pack_xy_i16(self.light[0][0], self.light[0][1]),
466 9 => pack_xy_i16(self.light[0][2], self.light[1][0]),
467 10 => pack_xy_i16(self.light[1][1], self.light[1][2]),
468 11 => pack_xy_i16(self.light[2][0], self.light[2][1]),
469 12 => sign_extend_16(self.light[2][2]),
470 13 => self.bg_color[0] as u32,
471 14 => self.bg_color[1] as u32,
472 15 => self.bg_color[2] as u32,
473 16 => pack_xy_i16(self.light_color[0][0], self.light_color[0][1]),
474 17 => pack_xy_i16(self.light_color[0][2], self.light_color[1][0]),
475 18 => pack_xy_i16(self.light_color[1][1], self.light_color[1][2]),
476 19 => pack_xy_i16(self.light_color[2][0], self.light_color[2][1]),
477 20 => sign_extend_16(self.light_color[2][2]),
478 21 => self.far_color[0] as u32,
479 22 => self.far_color[1] as u32,
480 23 => self.far_color[2] as u32,
481 24 => self.ofx as u32,
482 25 => self.ofy as u32,
483 26 => sign_extend_16(self.h as i16),
487 27 => sign_extend_16(self.dqa),
488 28 => self.dqb as u32,
489 29 => sign_extend_16(self.zsf3),
490 30 => sign_extend_16(self.zsf4),
491 31 => self.flag,
492 _ => unreachable!(),
493 }
494 }
495
496 pub fn write_control(&mut self, idx: u8, value: u32) {
498 let (lo, hi) = unpack_xy_i16(value);
499 match idx & 31 {
500 0 => {
501 self.rotation[0][0] = lo;
502 self.rotation[0][1] = hi;
503 }
504 1 => {
505 self.rotation[0][2] = lo;
506 self.rotation[1][0] = hi;
507 }
508 2 => {
509 self.rotation[1][1] = lo;
510 self.rotation[1][2] = hi;
511 }
512 3 => {
513 self.rotation[2][0] = lo;
514 self.rotation[2][1] = hi;
515 }
516 4 => self.rotation[2][2] = value as i16,
517 5 => self.translation[0] = value as i32,
518 6 => self.translation[1] = value as i32,
519 7 => self.translation[2] = value as i32,
520 8 => {
521 self.light[0][0] = lo;
522 self.light[0][1] = hi;
523 }
524 9 => {
525 self.light[0][2] = lo;
526 self.light[1][0] = hi;
527 }
528 10 => {
529 self.light[1][1] = lo;
530 self.light[1][2] = hi;
531 }
532 11 => {
533 self.light[2][0] = lo;
534 self.light[2][1] = hi;
535 }
536 12 => self.light[2][2] = value as i16,
537 13 => self.bg_color[0] = value as i32,
538 14 => self.bg_color[1] = value as i32,
539 15 => self.bg_color[2] = value as i32,
540 16 => {
541 self.light_color[0][0] = lo;
542 self.light_color[0][1] = hi;
543 }
544 17 => {
545 self.light_color[0][2] = lo;
546 self.light_color[1][0] = hi;
547 }
548 18 => {
549 self.light_color[1][1] = lo;
550 self.light_color[1][2] = hi;
551 }
552 19 => {
553 self.light_color[2][0] = lo;
554 self.light_color[2][1] = hi;
555 }
556 20 => self.light_color[2][2] = value as i16,
557 21 => self.far_color[0] = value as i32,
558 22 => self.far_color[1] = value as i32,
559 23 => self.far_color[2] = value as i32,
560 24 => self.ofx = value as i32,
561 25 => self.ofy = value as i32,
562 26 => self.h = value as u16,
563 27 => self.dqa = value as i16,
564 28 => self.dqb = value as i32,
565 29 => self.zsf3 = value as i16,
566 30 => self.zsf4 = value as i16,
567 31 => {
569 self.flag = value & 0x7FFF_F000;
570 self.update_flag_master();
571 }
572 _ => unreachable!(),
573 }
574 }
575
576 pub fn execute_with_stale_v0x(&mut self, instr: u32, stale_x: i16) {
583 self.stale_v0x = Some(stale_x);
584 self.execute(instr);
585 self.stale_v0x = None;
586 }
587
588 pub fn execute_op_with_stale_ir3(&mut self, instr: u32, stale_ir3: i16) {
591 self.stale_op_ir3 = Some(stale_ir3);
592 self.execute(instr);
593 self.stale_op_ir3 = None;
594 }
595
596 pub fn execute(&mut self, instr: u32) {
602 let cmd = Cmd::decode(instr);
603 let opcode = (instr & 0x3F) as u8;
604 if let Some(cycles) = gte_command_cycles(opcode) {
605 self.profile_ops = self.profile_ops.saturating_add(1);
606 self.profile_estimated_cycles =
607 self.profile_estimated_cycles.saturating_add(cycles as u64);
608 self.profile_opcode_counts[opcode as usize] =
609 self.profile_opcode_counts[opcode as usize].saturating_add(1);
610 }
611 self.flag = 0;
612 match opcode {
613 0x01 => self.op_rtps(cmd, 0, true),
614 0x06 => self.op_nclip(),
615 0x0C => self.op_op(cmd),
616 0x10 => self.op_dpcs(cmd, false),
617 0x11 => self.op_intpl(cmd),
618 0x12 => self.op_mvmva(cmd),
619 0x13 => self.op_ncds(cmd, 0),
620 0x14 => self.op_cdp(cmd),
621 0x16 => self.op_ncdt(cmd),
622 0x1B => self.op_nccs(cmd, 0),
623 0x1C => self.op_cc(cmd),
624 0x1E => self.op_ncs(cmd, 0),
625 0x20 => self.op_nct(cmd),
626 0x28 => self.op_sqr(cmd),
627 0x29 => self.op_dcpl(cmd),
628 0x2A => self.op_dpct(cmd),
629 0x2D => self.op_avsz3(),
630 0x2E => self.op_avsz4(),
631 0x30 => self.op_rtpt(cmd),
632 0x3D => self.op_gpf(cmd),
633 0x3E => self.op_gpl(cmd),
634 0x3F => self.op_ncct(cmd),
635 _ => {
636 }
641 }
642 self.update_flag_master();
643 }
644
645 fn op_rtps(&mut self, cmd: Cmd, idx: usize, last: bool) {
653 let mut v = self.v[idx];
654 if idx == 0 {
659 if let Some(stale_x) = self.stale_v0x {
660 v[0] = stale_x;
661 }
662 }
663 let sf = cmd.shift();
664
665 let tr = self.translation;
667 let rt = self.rotation;
668 let acc1 = self.mac_add_row(1, tr[0], &rt[0], &v, sf);
669 let acc2 = self.mac_add_row(2, tr[1], &rt[1], &v, sf);
670 let acc3 = self.mac_add_row(3, tr[2], &rt[2], &v, sf);
671 let mac1 = (acc1 >> sf) as i32;
672 let mac2 = (acc2 >> sf) as i32;
673 let mac3 = (acc3 >> sf) as i32;
674
675 self.ir[0] = self.saturate_ir(1, mac1, cmd.lm);
676 self.ir[1] = self.saturate_ir(2, mac2, cmd.lm);
677 let ir3_flag_value = (acc3 >> 12) as i32;
683 let _ = self.saturate_ir_flag_only(3, ir3_flag_value, false);
684 self.ir[2] = self.saturate_value_for_ir(mac3, cmd.lm);
685
686 let sz_value = (acc3 >> 12) as i32;
689 self.push_sz(sz_value);
690
691 let divisor = self.unr_divide();
693
694 let mac0_x = (divisor as i64) * (self.ir[0] as i64) + (self.ofx as i64);
696 let mac0_y = (divisor as i64) * (self.ir[1] as i64) + (self.ofy as i64);
697 let _ = self.check_mac0(mac0_x);
704 let sx = self.saturate_screen(((mac0_x >> 16) as i32) as i64, true);
705 let _ = self.check_mac0(mac0_y);
706 let sy = self.saturate_screen(((mac0_y >> 16) as i32) as i64, false);
707 self.push_sxy(sx, sy);
708
709 if last {
710 let mac0 = (divisor as i64) * (self.dqa as i64) + (self.dqb as i64);
712 self.mac0 = self.check_mac0(mac0);
713 self.ir0 = self.saturate_ir0((mac0 >> 12) as i32);
714 } else {
715 self.mac0 = self.check_mac0(mac0_y);
720 }
721 }
722
723 fn op_rtpt(&mut self, cmd: Cmd) {
725 self.op_rtps(cmd, 0, false);
726 self.op_rtps(cmd, 1, false);
727 self.op_rtps(cmd, 2, true);
728 }
729
730 fn op_nclip(&mut self) {
736 let sx0 = self.sxy[0][0] as i64;
737 let sy0 = self.sxy[0][1] as i64;
738 let sx1 = self.sxy[1][0] as i64;
739 let sy1 = self.sxy[1][1] as i64;
740 let sx2 = self.sxy[2][0] as i64;
741 let sy2 = self.sxy[2][1] as i64;
742 let result = sx0 * (sy1 - sy2) + sx1 * (sy2 - sy0) + sx2 * (sy0 - sy1);
743 self.mac0 = self.check_mac0(result);
744 }
745
746 fn op_op(&mut self, cmd: Cmd) {
750 let sf = cmd.shift();
751 let d1 = self.rotation[0][0] as i64;
752 let d2 = self.rotation[1][1] as i64;
753 let d3 = self.rotation[2][2] as i64;
754 let ir1 = self.ir[0] as i64;
755 let ir2 = self.ir[1] as i64;
756 let ir3 = self.ir[2] as i64;
757 let ir3_mac1 = i64::from(self.stale_op_ir3.unwrap_or(self.ir[2]));
758 let mac1 = self.check_mac(1, (ir3_mac1 * d2) - (ir2 * d3)) >> sf;
759 let mac2 = self.check_mac(2, (ir1 * d3) - (ir3 * d1)) >> sf;
760 let mac3 = self.check_mac(3, (ir2 * d1) - (ir1 * d2)) >> sf;
761 self.mac[0] = mac1 as i32;
762 self.mac[1] = mac2 as i32;
763 self.mac[2] = mac3 as i32;
764 self.ir[0] = self.saturate_ir(1, self.mac[0], cmd.lm);
765 self.ir[1] = self.saturate_ir(2, self.mac[1], cmd.lm);
766 self.ir[2] = self.saturate_ir(3, self.mac[2], cmd.lm);
767 }
768
769 fn op_mvmva(&mut self, cmd: Cmd) {
773 let sf = cmd.shift();
774 let mx = self.select_matrix(cmd.mx);
775 let v = self.select_vector(cmd.vx);
776 let tr = self.select_translation(cmd.cv);
777 let v_mac1 = match self.stale_v0x {
782 Some(stale_x) if cmd.vx == 0 => [stale_x, v[1], v[2]],
783 _ => v,
784 };
785
786 if cmd.cv == 2 {
797 for i in 0..3 {
798 let bias = (tr[i] as i64) << 12;
799 let vx_in = if i == 0 { v_mac1[0] } else { v[0] };
800 let prod = (mx[i][0] as i64) * (vx_in as i64);
801 let stage1 = self.check_mac((i + 1) as u8, bias + prod) >> sf;
802 let _ = self.saturate_ir_flag_only((i + 1) as u8, stage1 as i32, false);
803 }
804 for i in 0..3 {
805 let prod = (mx[i][1] as i64) * (v[1] as i64) + (mx[i][2] as i64) * (v[2] as i64);
809 let mac = self.check_mac((i + 1) as u8, prod) >> sf;
810 self.mac[i] = mac as i32;
811 self.ir[i] = self.saturate_ir((i + 1) as u8, mac as i32, cmd.lm);
812 }
813 return;
814 }
815
816 for i in 0..3 {
817 let v_row = if i == 0 { &v_mac1 } else { &v };
818 let acc = self.mac_add_row((i + 1) as u8, tr[i], &mx[i], v_row, sf);
819 self.ir[i] = self.saturate_ir((i + 1) as u8, (acc >> sf) as i32, cmd.lm);
820 }
821 }
822
823 fn op_sqr(&mut self, cmd: Cmd) {
825 let sf = cmd.shift();
826 for i in 0..3 {
827 let v = self.ir[i] as i64;
828 let mac = self.check_mac((i + 1) as u8, v * v) >> sf;
829 self.mac[i] = mac as i32;
830 self.ir[i] = self.saturate_ir((i + 1) as u8, mac as i32, cmd.lm);
831 }
832 }
833
834 fn op_avsz3(&mut self) {
837 let sum = (self.sz[1] as i64) + (self.sz[2] as i64) + (self.sz[3] as i64);
838 let mac0 = (self.zsf3 as i64) * sum;
839 self.mac0 = self.check_mac0(mac0);
840 self.otz = self.saturate_otz(mac0 >> 12);
841 }
842
843 fn op_avsz4(&mut self) {
845 let sum =
846 (self.sz[0] as i64) + (self.sz[1] as i64) + (self.sz[2] as i64) + (self.sz[3] as i64);
847 let mac0 = (self.zsf4 as i64) * sum;
848 self.mac0 = self.check_mac0(mac0);
849 self.otz = self.saturate_otz(mac0 >> 12);
850 }
851
852 fn op_dpcs(&mut self, cmd: Cmd, is_dpct: bool) {
857 let (r, g, b) = if is_dpct {
858 (
859 self.rgb_fifo[0][0],
860 self.rgb_fifo[0][1],
861 self.rgb_fifo[0][2],
862 )
863 } else {
864 (self.rgbc[0], self.rgbc[1], self.rgbc[2])
865 };
866 let sf = cmd.shift();
867 let bases = [r as i64, g as i64, b as i64];
872 let fc = self.far_color;
873 for i in 0..3 {
874 let base = bases[i] << 16;
875 let diff = ((fc[i] as i64) << 12) - base;
876 let temp = self.check_mac((i + 1) as u8, diff) >> sf;
877 let ir = self.saturate_value_for_ir(temp as i32, false);
878 let _ = self.saturate_ir_flag_only((i + 1) as u8, temp as i32, false);
879 let combined =
880 self.check_mac((i + 1) as u8, base + (self.ir0 as i64) * (ir as i64)) >> sf;
881 self.mac[i] = combined as i32;
882 self.ir[i] = self.saturate_ir((i + 1) as u8, combined as i32, cmd.lm);
883 }
884 self.push_color_from_mac(cmd);
885 }
886
887 fn op_dpct(&mut self, cmd: Cmd) {
890 for _ in 0..3 {
891 self.op_dpcs(cmd, true);
892 }
893 }
894
895 fn op_intpl(&mut self, cmd: Cmd) {
897 let sf = cmd.shift();
898 let fc = self.far_color;
899 let initial_ir = self.ir;
900 for i in 0..3 {
901 let base = (initial_ir[i] as i64) << 12;
902 let diff = ((fc[i] as i64) << 12) - base;
903 let temp = self.check_mac((i + 1) as u8, diff) >> sf;
904 let _ = self.saturate_ir_flag_only((i + 1) as u8, temp as i32, false);
905 let ir = self.saturate_value_for_ir(temp as i32, false);
906 let combined =
907 self.check_mac((i + 1) as u8, base + (self.ir0 as i64) * (ir as i64)) >> sf;
908 self.mac[i] = combined as i32;
909 self.ir[i] = self.saturate_ir((i + 1) as u8, combined as i32, cmd.lm);
910 }
911 self.push_color_from_mac(cmd);
912 }
913
914 fn op_ncs(&mut self, cmd: Cmd, idx: usize) {
923 let v = self.v[idx];
924 self.light_matrix_pass(&v, cmd);
926 self.color_matrix_pass(cmd);
928 self.push_color_from_mac(cmd);
929 }
930
931 fn op_nct(&mut self, cmd: Cmd) {
933 for i in 0..3 {
934 self.op_ncs(cmd, i);
935 }
936 }
937
938 fn op_ncds(&mut self, cmd: Cmd, idx: usize) {
943 let sf = cmd.shift();
944 let v = self.v[idx];
945 self.light_matrix_pass(&v, cmd);
946 self.color_matrix_pass(cmd);
947 let cs = [
949 (self.rgbc[0] as i64) << 4,
950 (self.rgbc[1] as i64) << 4,
951 (self.rgbc[2] as i64) << 4,
952 ];
953 for i in 0..3 {
954 let base = cs[i] * (self.ir[i] as i64);
955 let diff = ((self.far_color[i] as i64) << 12) - base;
956 let temp = self.check_mac((i + 1) as u8, diff) >> sf;
957 let _ = self.saturate_ir_flag_only((i + 1) as u8, temp as i32, false);
958 let ir = self.saturate_value_for_ir(temp as i32, false);
959 let combined =
960 self.check_mac((i + 1) as u8, base + (self.ir0 as i64) * (ir as i64)) >> sf;
961 self.mac[i] = combined as i32;
962 self.ir[i] = self.saturate_ir((i + 1) as u8, combined as i32, cmd.lm);
963 }
964 self.push_color_from_mac(cmd);
965 }
966
967 fn op_ncdt(&mut self, cmd: Cmd) {
969 for i in 0..3 {
970 self.op_ncds(cmd, i);
971 }
972 }
973
974 fn op_nccs(&mut self, cmd: Cmd, idx: usize) {
979 let sf = cmd.shift();
980 let v = self.v[idx];
981 self.light_matrix_pass(&v, cmd);
982 self.color_matrix_pass(cmd);
983 let cs = [
984 (self.rgbc[0] as i64) << 4,
985 (self.rgbc[1] as i64) << 4,
986 (self.rgbc[2] as i64) << 4,
987 ];
988 for i in 0..3 {
989 let mac = self.check_mac((i + 1) as u8, cs[i] * (self.ir[i] as i64)) >> sf;
990 self.mac[i] = mac as i32;
991 self.ir[i] = self.saturate_ir((i + 1) as u8, mac as i32, cmd.lm);
992 }
993 self.push_color_from_mac(cmd);
994 }
995
996 fn op_ncct(&mut self, cmd: Cmd) {
997 for i in 0..3 {
998 self.op_nccs(cmd, i);
999 }
1000 }
1001
1002 fn op_cc(&mut self, cmd: Cmd) {
1005 let sf = cmd.shift();
1006 self.color_matrix_pass(cmd);
1007 let cs = [
1008 (self.rgbc[0] as i64) << 4,
1009 (self.rgbc[1] as i64) << 4,
1010 (self.rgbc[2] as i64) << 4,
1011 ];
1012 for i in 0..3 {
1013 let mac = self.check_mac((i + 1) as u8, cs[i] * (self.ir[i] as i64)) >> sf;
1014 self.mac[i] = mac as i32;
1015 self.ir[i] = self.saturate_ir((i + 1) as u8, mac as i32, cmd.lm);
1016 }
1017 self.push_color_from_mac(cmd);
1018 }
1019
1020 fn op_cdp(&mut self, cmd: Cmd) {
1022 let sf = cmd.shift();
1023 self.color_matrix_pass(cmd);
1024 let cs = [
1025 (self.rgbc[0] as i64) << 4,
1026 (self.rgbc[1] as i64) << 4,
1027 (self.rgbc[2] as i64) << 4,
1028 ];
1029 for i in 0..3 {
1030 let base = cs[i] * (self.ir[i] as i64);
1031 let diff = ((self.far_color[i] as i64) << 12) - base;
1032 let temp = self.check_mac((i + 1) as u8, diff) >> sf;
1033 let _ = self.saturate_ir_flag_only((i + 1) as u8, temp as i32, false);
1034 let ir = self.saturate_value_for_ir(temp as i32, false);
1035 let combined =
1036 self.check_mac((i + 1) as u8, base + (self.ir0 as i64) * (ir as i64)) >> sf;
1037 self.mac[i] = combined as i32;
1038 self.ir[i] = self.saturate_ir((i + 1) as u8, combined as i32, cmd.lm);
1039 }
1040 self.push_color_from_mac(cmd);
1041 }
1042
1043 fn op_dcpl(&mut self, cmd: Cmd) {
1046 let sf = cmd.shift();
1047 let cs = [
1048 (self.rgbc[0] as i64) << 4,
1049 (self.rgbc[1] as i64) << 4,
1050 (self.rgbc[2] as i64) << 4,
1051 ];
1052 let ir = self.ir;
1053 for i in 0..3 {
1054 let base = cs[i] * (ir[i] as i64);
1055 let diff = ((self.far_color[i] as i64) << 12) - base;
1056 let temp = self.check_mac((i + 1) as u8, diff) >> sf;
1057 let _ = self.saturate_ir_flag_only((i + 1) as u8, temp as i32, false);
1058 let irc = self.saturate_value_for_ir(temp as i32, false);
1059 let combined =
1060 self.check_mac((i + 1) as u8, base + (self.ir0 as i64) * (irc as i64)) >> sf;
1061 self.mac[i] = combined as i32;
1062 self.ir[i] = self.saturate_ir((i + 1) as u8, combined as i32, cmd.lm);
1063 }
1064 self.push_color_from_mac(cmd);
1065 }
1066
1067 fn op_gpf(&mut self, cmd: Cmd) {
1070 let sf = cmd.shift();
1071 for i in 0..3 {
1072 let mac = self.check_mac((i + 1) as u8, (self.ir0 as i64) * (self.ir[i] as i64)) >> sf;
1073 self.mac[i] = mac as i32;
1074 self.ir[i] = self.saturate_ir((i + 1) as u8, mac as i32, cmd.lm);
1075 }
1076 self.push_color_from_mac(cmd);
1077 }
1078
1079 fn op_gpl(&mut self, cmd: Cmd) {
1081 let sf = cmd.shift();
1082 for i in 0..3 {
1083 let base = (self.mac[i] as i64) << sf;
1087 let mac = self.check_mac(
1088 (i + 1) as u8,
1089 base + (self.ir0 as i64) * (self.ir[i] as i64),
1090 ) >> sf;
1091 self.mac[i] = mac as i32;
1092 self.ir[i] = self.saturate_ir((i + 1) as u8, mac as i32, cmd.lm);
1093 }
1094 self.push_color_from_mac(cmd);
1095 }
1096
1097 fn mac_add_row(&mut self, idx: u8, tr: i32, row: &[i16; 3], v: &[i16; 3], sf: u32) -> i64 {
1106 let mut acc = self.check_mac(idx, ((tr as i64) << 12) + (row[0] as i64) * (v[0] as i64));
1116 acc = self.check_mac(idx, acc + (row[1] as i64) * (v[1] as i64));
1117 acc = self.check_mac(idx, acc + (row[2] as i64) * (v[2] as i64));
1118 self.mac[(idx - 1) as usize] = (acc >> sf) as i32;
1119 acc
1120 }
1121
1122 fn light_matrix_pass(&mut self, v: &[i16; 3], cmd: Cmd) {
1125 let sf = cmd.shift();
1126 for i in 0..3 {
1127 let row = self.light[i];
1128 let acc = self.mac_add_row((i + 1) as u8, 0, &row, v, sf);
1129 self.ir[i] = self.saturate_ir((i + 1) as u8, (acc >> sf) as i32, cmd.lm);
1130 }
1131 }
1132
1133 fn color_matrix_pass(&mut self, cmd: Cmd) {
1135 let sf = cmd.shift();
1136 let n = self.ir;
1137 for i in 0..3 {
1138 let row = self.light_color[i];
1139 let bk = self.bg_color[i];
1140 let acc = self.mac_add_row((i + 1) as u8, bk, &row, &n, sf);
1141 self.ir[i] = self.saturate_ir((i + 1) as u8, (acc >> sf) as i32, cmd.lm);
1142 }
1143 }
1144
1145 fn check_mac(&mut self, idx: u8, value: i64) -> i64 {
1149 let pos_limit = (1i64 << 43) - 1;
1150 let neg_limit = -(1i64 << 43);
1151 if value > pos_limit {
1152 self.flag |= match idx {
1153 1 => flag::MAC1_POS,
1154 2 => flag::MAC2_POS,
1155 3 => flag::MAC3_POS,
1156 _ => 0,
1157 };
1158 }
1159 if value < neg_limit {
1160 self.flag |= match idx {
1161 1 => flag::MAC1_NEG,
1162 2 => flag::MAC2_NEG,
1163 3 => flag::MAC3_NEG,
1164 _ => 0,
1165 };
1166 }
1167 (value << 20) >> 20
1168 }
1169
1170 fn check_mac0(&mut self, value: i64) -> i32 {
1172 if value > i32::MAX as i64 {
1173 self.flag |= flag::MAC0_POS;
1174 }
1175 if value < i32::MIN as i64 {
1176 self.flag |= flag::MAC0_NEG;
1177 }
1178 value as i32
1179 }
1180
1181 fn saturate_ir(&mut self, idx: u8, value: i32, lm: bool) -> i16 {
1184 let lo = if lm { 0 } else { -0x8000 };
1185 let hi = 0x7FFFi32;
1186 let sat = if value > hi {
1187 self.set_ir_flag(idx);
1188 hi
1189 } else if value < lo {
1190 self.set_ir_flag(idx);
1191 lo
1192 } else {
1193 value
1194 };
1195 sat as i16
1196 }
1197
1198 fn saturate_ir_flag_only(&mut self, idx: u8, value: i32, lm: bool) -> i16 {
1202 let lo = if lm { 0 } else { -0x8000 };
1203 let hi = 0x7FFFi32;
1204 if value > hi || value < lo {
1205 self.set_ir_flag(idx);
1206 }
1207 value.clamp(lo, hi) as i16
1208 }
1209
1210 fn saturate_value_for_ir(&self, value: i32, lm: bool) -> i16 {
1213 let lo = if lm { 0 } else { -0x8000 };
1214 value.clamp(lo, 0x7FFF) as i16
1215 }
1216
1217 fn set_ir_flag(&mut self, idx: u8) {
1218 self.flag |= match idx {
1219 1 => flag::IR1_SAT,
1220 2 => flag::IR2_SAT,
1221 3 => flag::IR3_SAT,
1222 _ => 0,
1223 };
1224 }
1225
1226 fn saturate_ir0(&mut self, value: i32) -> i16 {
1227 if value < 0 {
1228 self.flag |= flag::IR0_SAT;
1229 0
1230 } else if value > 0x1000 {
1231 self.flag |= flag::IR0_SAT;
1232 0x1000
1233 } else {
1234 value as i16
1235 }
1236 }
1237
1238 fn saturate_screen(&mut self, value: i64, is_x: bool) -> i16 {
1241 let lo = -0x400i64;
1242 let hi = 0x3FFi64;
1243 let bit = if is_x { flag::SX2_SAT } else { flag::SY2_SAT };
1244 if value > hi {
1245 self.flag |= bit;
1246 hi as i16
1247 } else if value < lo {
1248 self.flag |= bit;
1249 lo as i16
1250 } else {
1251 value as i16
1252 }
1253 }
1254
1255 fn saturate_otz(&mut self, value: i64) -> u16 {
1256 if value < 0 {
1257 self.flag |= flag::SZ3_OTZ_SAT;
1258 0
1259 } else if value > 0xFFFF {
1260 self.flag |= flag::SZ3_OTZ_SAT;
1261 0xFFFF
1262 } else {
1263 value as u16
1264 }
1265 }
1266
1267 fn push_sz(&mut self, value: i32) {
1269 let z = if value < 0 {
1270 self.flag |= flag::SZ3_OTZ_SAT;
1271 0
1272 } else if value > 0xFFFF {
1273 self.flag |= flag::SZ3_OTZ_SAT;
1274 0xFFFF
1275 } else {
1276 value as u16
1277 };
1278 self.sz[0] = self.sz[1];
1279 self.sz[1] = self.sz[2];
1280 self.sz[2] = self.sz[3];
1281 self.sz[3] = z;
1282 }
1283
1284 fn push_sxy(&mut self, x: i16, y: i16) {
1286 self.sxy[0] = self.sxy[1];
1287 self.sxy[1] = self.sxy[2];
1288 self.sxy[2] = [x, y];
1289 }
1290
1291 fn push_color_from_mac(&mut self, _cmd: Cmd) {
1294 let r = self.saturate_color(self.mac[0] >> 4, flag::COLOR_R_SAT);
1295 let g = self.saturate_color(self.mac[1] >> 4, flag::COLOR_G_SAT);
1296 let b = self.saturate_color(self.mac[2] >> 4, flag::COLOR_B_SAT);
1297 let code = self.rgbc[3];
1298 self.rgb_fifo[0] = self.rgb_fifo[1];
1299 self.rgb_fifo[1] = self.rgb_fifo[2];
1300 self.rgb_fifo[2] = [r, g, b, code];
1301 }
1302
1303 fn saturate_color(&mut self, value: i32, flag_bit: u32) -> u8 {
1304 if value < 0 {
1305 self.flag |= flag_bit;
1306 0
1307 } else if value > 0xFF {
1308 self.flag |= flag_bit;
1309 0xFF
1310 } else {
1311 value as u8
1312 }
1313 }
1314
1315 fn unr_divide(&mut self) -> u32 {
1318 let h = self.h as u32;
1319 let sz3 = self.sz[3] as u32;
1320 if h >= sz3 * 2 {
1321 self.flag |= flag::DIV_OVERFLOW;
1322 return 0x1FFFF;
1323 }
1324 let z = (sz3 as u16).leading_zeros();
1325 let n = h << z;
1326 let d = sz3 << z;
1327 let table_index = (d.wrapping_sub(0x7FC0) >> 7) as usize;
1336 let u = (UNR_TABLE[table_index] as u32) + 0x101;
1337 let d = (0x2000080u32.wrapping_sub(d.wrapping_mul(u))) >> 8;
1338 let d = (0x80u32.wrapping_add(d.wrapping_mul(u))) >> 8;
1339 let result = (((n as u64) * (d as u64)) + 0x8000) >> 16;
1340 cmp::min(0x1FFFF, result as u32)
1341 }
1342
1343 fn update_flag_master(&mut self) {
1345 if self.flag & flag::ERROR_MASK != 0 {
1346 self.flag |= 1 << 31;
1347 }
1348 }
1349
1350 fn select_matrix(&self, mx: u8) -> [[i16; 3]; 3] {
1351 match mx {
1352 0 => self.rotation,
1353 1 => self.light,
1354 2 => self.light_color,
1355 _ => {
1361 let r = (self.rgbc[0] as i16) << 4;
1362 [
1363 [-r, r, self.ir0],
1364 [
1365 self.rotation[0][2],
1366 self.rotation[0][2],
1367 self.rotation[0][2],
1368 ],
1369 [
1370 self.rotation[1][1],
1371 self.rotation[1][1],
1372 self.rotation[1][1],
1373 ],
1374 ]
1375 }
1376 }
1377 }
1378
1379 fn select_vector(&self, vx: u8) -> [i16; 3] {
1380 match vx {
1381 0 => self.v[0],
1382 1 => self.v[1],
1383 2 => self.v[2],
1384 _ => self.ir,
1385 }
1386 }
1387
1388 fn select_translation(&self, cv: u8) -> [i32; 3] {
1389 match cv {
1390 0 => self.translation,
1391 1 => self.bg_color,
1392 2 => self.far_color,
1393 _ => [0, 0, 0],
1394 }
1395 }
1396}
1397
1398impl Default for Gte {
1399 fn default() -> Self {
1400 Self::new()
1401 }
1402}
1403
1404fn pack_xy_i16(x: i16, y: i16) -> u32 {
1409 ((y as u16 as u32) << 16) | (x as u16 as u32)
1410}
1411
1412fn unpack_xy_i16(value: u32) -> (i16, i16) {
1414 (value as i16, (value >> 16) as i16)
1415}
1416
1417fn sign_extend_16(value: i16) -> u32 {
1418 value as i32 as u32
1419}
1420
1421fn pack_irgb(ir: &[i16; 3]) -> u32 {
1422 let r = ir[0].clamp(0, 0xF80) >> 7;
1423 let g = ir[1].clamp(0, 0xF80) >> 7;
1424 let b = ir[2].clamp(0, 0xF80) >> 7;
1425 ((b as u32) << 10) | ((g as u32) << 5) | (r as u32)
1426}
1427
1428fn leading_count_signed(value: u32) -> u32 {
1432 let test = if (value as i32) < 0 { !value } else { value };
1433 if test == 0 {
1434 32
1435 } else {
1436 test.leading_zeros()
1437 }
1438}
1439
1440#[rustfmt::skip]
1448static UNR_TABLE: [u8; 257] = [
1449 0xFF,0xFD,0xFB,0xF9,0xF7,0xF5,0xF3,0xF1, 0xEF,0xEE,0xEC,0xEA,0xE8,0xE6,0xE4,0xE3,
1450 0xE1,0xDF,0xDD,0xDC,0xDA,0xD8,0xD6,0xD5, 0xD3,0xD1,0xD0,0xCE,0xCD,0xCB,0xC9,0xC8,
1451 0xC6,0xC5,0xC3,0xC1,0xC0,0xBE,0xBD,0xBB, 0xBA,0xB8,0xB7,0xB5,0xB4,0xB2,0xB1,0xB0,
1452 0xAE,0xAD,0xAB,0xAA,0xA9,0xA7,0xA6,0xA4, 0xA3,0xA2,0xA0,0x9F,0x9E,0x9C,0x9B,0x9A,
1453 0x99,0x97,0x96,0x95,0x94,0x92,0x91,0x90, 0x8F,0x8D,0x8C,0x8B,0x8A,0x89,0x87,0x86,
1454 0x85,0x84,0x83,0x82,0x81,0x7F,0x7E,0x7D, 0x7C,0x7B,0x7A,0x79,0x78,0x77,0x75,0x74,
1455 0x73,0x72,0x71,0x70,0x6F,0x6E,0x6D,0x6C, 0x6B,0x6A,0x69,0x68,0x67,0x66,0x65,0x64,
1456 0x63,0x62,0x61,0x60,0x5F,0x5E,0x5D,0x5D, 0x5C,0x5B,0x5A,0x59,0x58,0x57,0x56,0x55,
1457 0x54,0x53,0x53,0x52,0x51,0x50,0x4F,0x4E, 0x4D,0x4D,0x4C,0x4B,0x4A,0x49,0x48,0x48,
1458 0x47,0x46,0x45,0x44,0x43,0x43,0x42,0x41, 0x40,0x3F,0x3F,0x3E,0x3D,0x3C,0x3C,0x3B,
1459 0x3A,0x39,0x39,0x38,0x37,0x36,0x36,0x35, 0x34,0x33,0x33,0x32,0x31,0x31,0x30,0x2F,
1460 0x2E,0x2E,0x2D,0x2C,0x2C,0x2B,0x2A,0x2A, 0x29,0x28,0x28,0x27,0x26,0x26,0x25,0x24,
1461 0x24,0x23,0x22,0x22,0x21,0x20,0x20,0x1F, 0x1E,0x1E,0x1D,0x1D,0x1C,0x1B,0x1B,0x1A,
1462 0x19,0x19,0x18,0x18,0x17,0x16,0x16,0x15, 0x15,0x14,0x14,0x13,0x12,0x12,0x11,0x11,
1463 0x10,0x0F,0x0F,0x0E,0x0E,0x0D,0x0D,0x0C, 0x0C,0x0B,0x0A,0x0A,0x09,0x09,0x08,0x08,
1464 0x07,0x07,0x06,0x06,0x05,0x05,0x04,0x04, 0x03,0x03,0x02,0x02,0x01,0x01,0x00,0x00,
1465 0x00,
1466];
1467
1468#[cfg(test)]
1469mod tests {
1470 use super::*;
1471
1472 #[test]
1479 fn mvmva_stale_v0x_reproduces_hwb010_compose_column() {
1480 let seed = |g: &mut Gte| {
1481 g.write_control(0, 0x0000_0F6D);
1483 g.write_control(1, 0x0125_FBC1);
1484 g.write_control(2, 0x0427_F098);
1485 g.write_control(3, 0xFBB1_FBEA);
1486 g.write_control(4, 0xFFFF_F125);
1487 g.write_control(5, 0);
1488 g.write_control(6, 0);
1489 g.write_control(7, 0);
1490 g.write_data(0, 0x0218_FFE5);
1492 g.write_data(1, 0x0000_0008);
1493 };
1494 const MVMVA_RT_V0_TR_SF1: u32 = 0x4A08_0012;
1495
1496 let mut g = Gte::new();
1498 seed(&mut g);
1499 g.execute(MVMVA_RT_V0_TR_SF1);
1500 assert_eq!(g.read_data(25), 0xFFFF_FFE3); assert_eq!(g.read_data(26), 0xFFFF_FDFC); assert_eq!(g.read_data(27), 0xFFFF_FF6F); let mut g = Gte::new();
1507 seed(&mut g);
1508 g.execute_with_stale_v0x(MVMVA_RT_V0_TR_SF1, 0x0218);
1509 assert_eq!(g.read_data(25), 0x0000_0202); assert_eq!(g.read_data(26), 0xFFFF_FDFC);
1511 assert_eq!(g.read_data(27), 0xFFFF_FF6F);
1512
1513 g.execute(MVMVA_RT_V0_TR_SF1);
1515 assert_eq!(g.read_data(25), 0xFFFF_FFE3);
1516 }
1517
1518 #[test]
1519 fn rtps_stale_v0x_reproduces_unpadded_projection_hazard() {
1520 let seed = |g: &mut Gte| {
1521 g.write_control(0, 0x0000_1000);
1524 g.write_control(2, 0x0000_1000);
1525 g.write_control(4, 0x0000_1000);
1526 g.write_control(26, 160);
1527 g.write_data(0, 10);
1528 g.write_data(1, 160);
1529 };
1530 const RTPS_SF1: u32 = 0x4A08_0001;
1531
1532 let mut clean = Gte::new();
1533 seed(&mut clean);
1534 clean.execute(RTPS_SF1);
1535 assert_eq!(clean.read_data(14) as u16 as i16, 10);
1536
1537 let mut hazarded = Gte::new();
1538 seed(&mut hazarded);
1539 hazarded.execute_with_stale_v0x(RTPS_SF1, -10);
1540 assert_eq!(hazarded.read_data(14) as u16 as i16, -10);
1541 }
1542
1543 #[test]
1544 fn reset_state_is_zero() {
1545 let g = Gte::new();
1546 for i in 0..32 {
1547 assert_eq!(g.read_data(i), 0, "data reg {i}");
1551 }
1552 for i in 0..31 {
1553 assert_eq!(g.read_control(i), 0, "ctrl reg {i} should reset to 0");
1554 }
1555 }
1556
1557 #[test]
1558 fn data_reg_v0_xy_round_trip() {
1559 let mut g = Gte::new();
1560 g.write_data(0, 0xDEAD_BEEF);
1561 assert_eq!(g.read_data(0), 0xDEAD_BEEF);
1563 }
1564
1565 #[test]
1566 fn data_reg_vz0_sign_extends_on_read() {
1567 let mut g = Gte::new();
1568 g.write_data(1, 0x0000_8000);
1569 assert_eq!(g.read_data(1), 0xFFFF_8000);
1570 }
1571
1572 #[test]
1573 fn sxyp_write_pushes_fifo() {
1574 let mut g = Gte::new();
1575 g.write_data(12, pack_xy_i16(1, 2)); g.write_data(13, pack_xy_i16(3, 4)); g.write_data(14, pack_xy_i16(5, 6)); g.write_data(15, pack_xy_i16(7, 8)); assert_eq!(g.read_data(12), pack_xy_i16(3, 4));
1580 assert_eq!(g.read_data(13), pack_xy_i16(5, 6));
1581 assert_eq!(g.read_data(14), pack_xy_i16(7, 8));
1582 }
1583
1584 #[test]
1585 fn lzcs_write_recomputes_lzcr() {
1586 let mut g = Gte::new();
1587 g.write_data(30, 0x0000_FFFF);
1588 assert_eq!(g.read_data(31), 16);
1589 g.write_data(30, 0xFFFF_0000);
1590 assert_eq!(g.read_data(31), 16);
1592 g.write_data(30, 0);
1593 assert_eq!(g.read_data(31), 32);
1594 g.write_data(30, !0);
1595 assert_eq!(g.read_data(31), 32);
1596 }
1597
1598 #[test]
1599 fn ctrl_h_reads_back_sign_extended() {
1600 let mut g = Gte::new();
1601 g.write_control(26, 0x0000_8000);
1602 assert_eq!(g.read_control(26), 0xFFFF_8000);
1603 }
1604
1605 #[test]
1606 fn flag_reset_at_start_of_each_op() {
1607 let mut g = Gte::new();
1608 g.write_control(31, flag::MAC1_POS);
1610 assert_eq!(g.read_control(31) & flag::MAC1_POS, flag::MAC1_POS);
1611 g.execute(0x06); assert_eq!(g.read_control(31), 0);
1614 }
1615
1616 #[test]
1617 fn rtps_identity_transform_against_origin() {
1618 let mut g = Gte::new();
1622 g.write_control(0, pack_xy_i16(0x1000, 0));
1624 g.write_control(1, pack_xy_i16(0, 0x1000));
1625 g.write_control(2, pack_xy_i16(0, 0));
1626 g.write_control(3, pack_xy_i16(0, 0));
1627 g.write_control(4, 0x1000);
1628 g.execute(0x0180_0001); assert!(g.read_control(31) & flag::DIV_OVERFLOW != 0);
1632 }
1633
1634 #[test]
1635 fn nclip_computes_z_cross_product() {
1636 let mut g = Gte::new();
1639 g.write_data(12, pack_xy_i16(0, 0));
1640 g.write_data(13, pack_xy_i16(10, 0));
1641 g.write_data(14, pack_xy_i16(0, 10));
1642 g.execute(0x06); assert_eq!(g.read_data(24) as i32, 100);
1644 }
1645
1646 #[test]
1647 fn avsz3_averages_with_zsf3() {
1648 let mut g = Gte::new();
1649 g.write_data(17, 0x0100); g.write_data(18, 0x0200); g.write_data(19, 0x0300); g.write_control(29, 0x0555);
1655 g.execute(0x2D); assert_eq!(g.read_data(24) as i32, 0x1FFE00);
1659 assert_eq!(g.read_data(7), 0x1FF);
1660 }
1661
1662 #[test]
1663 fn sqr_squares_ir() {
1664 let mut g = Gte::new();
1665 g.write_data(9, 0x10);
1666 g.write_data(10, 0x20);
1667 g.write_data(11, 0x30);
1668 g.execute(0x28); assert_eq!(g.read_data(25), 0x100);
1670 assert_eq!(g.read_data(26), 0x400);
1671 assert_eq!(g.read_data(27), 0x900);
1672 }
1673
1674 #[test]
1675 fn op_decode_extracts_command_word_fields() {
1676 let instr = (1u32 << 19) | (1 << 10) | (2 << 17) | (1 << 15) | 0x12;
1679 let cmd = Cmd::decode(instr);
1680 assert!(cmd.sf);
1681 assert!(cmd.lm);
1682 assert_eq!(cmd.mx, 2);
1683 assert_eq!(cmd.vx, 1);
1684 assert_eq!(cmd.cv, 0);
1685 }
1686
1687 #[test]
1688 fn unknown_function_is_silently_ignored() {
1689 let mut g = Gte::new();
1690 g.execute(0x00); assert_eq!(g.read_control(31), 0);
1692 }
1693
1694 #[test]
1695 fn profile_counts_recognised_commands_only() {
1696 let mut g = Gte::new();
1697
1698 g.execute(0x01); g.execute(0x30); g.execute(0x00); let profile = g.profile_snapshot();
1703 assert_eq!(profile.ops, 2);
1704 assert_eq!(profile.estimated_cycles, 15 + 23);
1705 assert_eq!(profile.opcode_counts[0x01], 1);
1706 assert_eq!(profile.opcode_counts[0x30], 1);
1707 assert_eq!(profile.opcode_counts[0x00], 0);
1708 }
1709
1710 fn install_identity_rotation(g: &mut Gte) {
1721 g.write_control(0, pack_xy_i16(0x1000, 0));
1722 g.write_control(1, pack_xy_i16(0, 0));
1723 g.write_control(2, pack_xy_i16(0x1000, 0));
1724 g.write_control(3, pack_xy_i16(0, 0));
1725 g.write_control(4, 0x1000);
1726 }
1727
1728 fn cmd_word(sf: bool, lm: bool, mx: u8, vx: u8, cv: u8, opcode: u8) -> u32 {
1732 ((sf as u32) << 19)
1733 | ((mx as u32 & 3) << 17)
1734 | ((vx as u32 & 3) << 15)
1735 | ((cv as u32 & 3) << 13)
1736 | ((lm as u32) << 10)
1737 | (opcode as u32)
1738 }
1739
1740 #[test]
1741 fn rtps_with_h_zero_projects_to_screen_offset() {
1742 let mut g = Gte::new();
1747 install_identity_rotation(&mut g);
1748 g.write_data(0, pack_xy_i16(1, 2)); g.write_data(1, 3); g.write_control(24, 0x0100_0000); g.write_control(25, 0x0200_0000); g.write_control(26, 0); g.write_control(27, 100); g.write_control(28, 5000); g.execute(cmd_word(true, false, 0, 0, 0, 0x01));
1756 assert_eq!(g.read_data(25) as i32, 1, "MAC1");
1758 assert_eq!(g.read_data(26) as i32, 2, "MAC2");
1759 assert_eq!(g.read_data(27) as i32, 3, "MAC3");
1760 assert_eq!(g.read_data(9), 1, "IR1");
1761 assert_eq!(g.read_data(10), 2, "IR2");
1762 assert_eq!(g.read_data(11), 3, "IR3");
1763 assert_eq!(g.read_data(19), 3, "SZ3");
1765 assert_eq!(g.read_data(14), pack_xy_i16(0x100, 0x200), "SXY2");
1767 assert_eq!(g.read_data(24) as i32, 5000, "MAC0");
1769 assert_eq!(g.read_data(8), 1, "IR0");
1771 assert_eq!(g.read_control(31) & flag::DIV_OVERFLOW, 0);
1773 }
1774
1775 #[test]
1776 fn rtpt_transforms_all_three_vertices_and_pushes_sz_fifo() {
1777 let mut g = Gte::new();
1778 install_identity_rotation(&mut g);
1779 g.write_data(0, pack_xy_i16(1, 2));
1780 g.write_data(1, 3); g.write_data(2, pack_xy_i16(4, 5));
1782 g.write_data(3, 6); g.write_data(4, pack_xy_i16(7, 8));
1784 g.write_data(5, 9); g.write_control(24, 0);
1786 g.write_control(25, 0);
1787 g.write_control(26, 0); g.execute(cmd_word(true, false, 0, 0, 0, 0x30));
1789 assert_eq!(g.read_data(9), 7);
1791 assert_eq!(g.read_data(10), 8);
1792 assert_eq!(g.read_data(11), 9);
1793 assert_eq!(g.read_data(17), 3, "SZ1");
1795 assert_eq!(g.read_data(18), 6, "SZ2");
1796 assert_eq!(g.read_data(19), 9, "SZ3");
1797 }
1798
1799 #[test]
1800 fn mvmva_rt_v0_tr_computes_rotation_plus_translation() {
1801 let mut g = Gte::new();
1802 install_identity_rotation(&mut g);
1803 g.write_data(0, pack_xy_i16(1, 2));
1804 g.write_data(1, 3);
1805 g.write_control(5, 10);
1806 g.write_control(6, 20);
1807 g.write_control(7, 30);
1808 g.execute(cmd_word(true, false, 0, 0, 0, 0x12));
1809 assert_eq!(g.read_data(25) as i32, 11, "MAC1 = TR0 + V0.x");
1811 assert_eq!(g.read_data(26) as i32, 22, "MAC2 = TR1 + V0.y");
1812 assert_eq!(g.read_data(27) as i32, 33, "MAC3 = TR2 + V0.z");
1813 assert_eq!(g.read_data(9), 11);
1814 assert_eq!(g.read_data(10), 22);
1815 assert_eq!(g.read_data(11), 33);
1816 }
1817
1818 #[test]
1819 fn mvmva_cv3_drops_translation() {
1820 let mut g = Gte::new();
1821 install_identity_rotation(&mut g);
1822 g.write_data(0, pack_xy_i16(1, 2));
1823 g.write_data(1, 3);
1824 g.write_control(5, 100); g.write_control(6, 200);
1826 g.write_control(7, 300);
1827 g.execute(cmd_word(true, false, 0, 0, 3, 0x12));
1828 assert_eq!(g.read_data(25) as i32, 1, "MAC1 = V.x (no TR)");
1829 assert_eq!(g.read_data(26) as i32, 2, "MAC2 = V.y");
1830 assert_eq!(g.read_data(27) as i32, 3, "MAC3 = V.z");
1831 }
1832
1833 #[test]
1834 fn avsz4_averages_using_zsf4() {
1835 let mut g = Gte::new();
1836 g.write_data(16, 0x010);
1837 g.write_data(17, 0x020);
1838 g.write_data(18, 0x030);
1839 g.write_data(19, 0x040);
1840 g.write_control(30, 0x0400); g.execute(0x2E);
1842 assert_eq!(g.read_data(24) as i32, 0x28000, "MAC0");
1844 assert_eq!(g.read_data(7), 0x28, "OTZ = MAC0>>12");
1845 }
1846
1847 #[test]
1848 fn irgb_write_unpacks_into_ir_vector() {
1849 let mut g = Gte::new();
1852 g.write_data(28, (0x0A << 10) | (0x10 << 5) | 0x1F);
1853 assert_eq!(g.read_data(9) as i16, 0x1F << 7);
1854 assert_eq!(g.read_data(10) as i16, 0x10 << 7);
1855 assert_eq!(g.read_data(11) as i16, 0x0A << 7);
1856 }
1857
1858 #[test]
1859 fn orgb_packs_ir_into_5_5_5() {
1860 let mut g = Gte::new();
1861 g.write_data(9, 0); g.write_data(10, 0xF80); g.write_data(11, 0x780); assert_eq!(g.read_data(29), (0x0F << 10) | (0x1F << 5));
1865 }
1866
1867 #[test]
1868 fn orgb_write_is_read_only() {
1869 let mut g = Gte::new();
1870 g.write_data(9, 0x500);
1871 g.write_data(29, 0xFFFF); assert_eq!(g.read_data(9) as i16, 0x500);
1873 }
1874
1875 #[test]
1876 fn mac_overflow_sets_positive_flag() {
1877 let mut g = Gte::new();
1880 install_identity_rotation(&mut g);
1881 g.write_control(5, 0x7FFF_FFFF); g.write_control(0, pack_xy_i16(0x7FFF, 0)); g.write_data(0, pack_xy_i16(0x7FFF, 0)); g.execute(cmd_word(false, false, 0, 0, 0, 0x01));
1885 assert!(g.read_control(31) & flag::MAC1_POS != 0, "MAC1_POS");
1886 assert!(g.read_control(31) & 0x8000_0000 != 0, "master bit");
1887 }
1888
1889 #[test]
1890 fn gpf_multiplies_ir_by_ir0_and_pushes_color() {
1891 let mut g = Gte::new();
1892 g.write_data(8, 0x1000); g.write_data(9, 0x800);
1894 g.write_data(10, 0x1000); g.write_data(11, 0x200);
1896 g.execute(cmd_word(true, false, 0, 0, 0, 0x3D));
1897 assert_eq!(g.read_data(25) as i32, 0x800, "MAC1");
1898 assert_eq!(g.read_data(26) as i32, 0x1000, "MAC2");
1899 assert_eq!(g.read_data(27) as i32, 0x200, "MAC3");
1900 assert_eq!(g.read_data(22), 0x0020_FF80);
1903 assert!(g.read_control(31) & flag::COLOR_G_SAT != 0);
1904 }
1905
1906 #[test]
1907 fn ncs_does_not_modulate_by_rgbc() {
1908 let mut g = Gte::new();
1914 g.write_data(0, pack_xy_i16(0x1000, 0));
1915 g.write_data(1, 0);
1916 g.write_data(6, 0x0001_0101); g.write_control(8, pack_xy_i16(0x1000, 0));
1920 g.write_control(16, pack_xy_i16(0x0400, 0));
1921
1922 g.execute(cmd_word(true, false, 0, 0, 0, 0x1E)); assert_eq!(g.read_data(25) as i32, 0x0400, "MAC1 after LCM");
1925 assert_eq!(g.read_data(22), 0x0000_0040, "RGB2 should ignore tiny RGBC");
1926 }
1927
1928 #[test]
1929 fn dpcs_far_color_uses_twelve_bit_fractional_scale() {
1930 let mut g = Gte::new();
1931 g.write_data(6, 0x0000_0000); g.write_data(8, 0x1000); g.write_control(21, 0x0100); g.write_control(22, 0x0200); g.write_control(23, 0x0300); g.execute(cmd_word(true, false, 0, 0, 0, 0x10)); assert_eq!(g.read_data(25) as i32, 0x0100, "MAC1");
1940 assert_eq!(g.read_data(26) as i32, 0x0200, "MAC2");
1941 assert_eq!(g.read_data(27) as i32, 0x0300, "MAC3");
1942 assert_eq!(g.read_data(22), 0x0030_2010, "RGB2");
1943 }
1944
1945 #[test]
1946 fn intpl_far_color_uses_twelve_bit_fractional_scale() {
1947 let mut g = Gte::new();
1948 g.write_data(8, 0x1000); g.write_data(9, 0);
1950 g.write_data(10, 0);
1951 g.write_data(11, 0);
1952 g.write_control(21, 0x0100); g.write_control(22, 0x0200); g.write_control(23, 0x0300); g.execute(cmd_word(true, false, 0, 0, 0, 0x11)); assert_eq!(g.read_data(25) as i32, 0x0100, "MAC1");
1959 assert_eq!(g.read_data(26) as i32, 0x0200, "MAC2");
1960 assert_eq!(g.read_data(27) as i32, 0x0300, "MAC3");
1961 assert_eq!(g.read_data(22), 0x0030_2010, "RGB2");
1962 }
1963
1964 #[test]
1965 fn gpl_accumulates_base_mac_into_product() {
1966 let mut g = Gte::new();
1969 g.write_data(25, 0x100);
1970 g.write_data(26, 0x200);
1971 g.write_data(27, 0x300);
1972 g.write_data(8, 0x1000); g.write_data(9, 0x400);
1974 g.write_data(10, 0x500);
1975 g.write_data(11, 0x600);
1976 g.execute(cmd_word(true, false, 0, 0, 0, 0x3E));
1977 assert_eq!(g.read_data(25) as i32, 0x500, "MAC1 = 0x100 + 0x400");
1978 assert_eq!(g.read_data(26) as i32, 0x700, "MAC2 = 0x200 + 0x500");
1979 assert_eq!(g.read_data(27) as i32, 0x900, "MAC3 = 0x300 + 0x600");
1980 }
1981
1982 #[test]
1983 fn flag_master_bit_set_on_any_error() {
1984 let mut g = Gte::new();
1985 g.write_control(31, flag::IR1_SAT);
1986 assert_eq!(g.read_control(31) & flag::IR1_SAT, flag::IR1_SAT);
1987 assert!(g.read_control(31) & 0x8000_0000 != 0);
1988 }
1989
1990 #[test]
1991 fn flag_master_bit_clear_on_non_error_bits() {
1992 let mut g = Gte::new();
1998 g.write_control(31, 0);
1999 assert_eq!(g.read_control(31) & 0x8000_0000, 0);
2000 }
2001
2002 #[test]
2003 fn nclip_collinear_points_produce_zero() {
2004 let mut g = Gte::new();
2006 g.write_data(12, pack_xy_i16(0, 0));
2007 g.write_data(13, pack_xy_i16(5, 5));
2008 g.write_data(14, pack_xy_i16(10, 10));
2009 g.execute(0x06);
2010 assert_eq!(g.read_data(24) as i32, 0);
2011 }
2012
2013 #[test]
2014 fn op_outer_product_of_ir_with_unit_diagonal() {
2015 let mut g = Gte::new();
2018 g.write_control(0, pack_xy_i16(0x1000, 0));
2019 g.write_control(2, pack_xy_i16(0x1000, 0));
2020 g.write_control(4, 0x1000);
2021 g.write_data(9, 3);
2022 g.write_data(10, 4);
2023 g.write_data(11, 5);
2024 g.execute(cmd_word(true, false, 0, 0, 0, 0x0C));
2025 assert_eq!(g.read_data(25) as i32, 1, "MAC1");
2029 assert_eq!(g.read_data(26) as i32, -2, "MAC2");
2030 assert_eq!(g.read_data(27) as i32, 1, "MAC3");
2031 assert_eq!(g.read_data(9) as i16, 1);
2032 assert_eq!(g.read_data(10) as i16, -2);
2033 assert_eq!(g.read_data(11) as i16, 1);
2034 }
2035
2036 #[test]
2037 fn ir0_saturates_to_upper_bound() {
2038 let mut g = Gte::new();
2042 install_identity_rotation(&mut g);
2043 g.write_data(0, pack_xy_i16(0, 0));
2044 g.write_data(1, 1); g.write_control(26, 1); g.write_control(27, 0x7FFF); g.write_control(28, 0); g.execute(cmd_word(true, false, 0, 0, 0, 0x01));
2049 assert_eq!(g.read_data(8) as i16, 0x1000, "IR0 saturated");
2050 assert!(g.read_control(31) & flag::IR0_SAT != 0);
2051 }
2052
2053 #[test]
2054 fn color_fifo_push_order_across_three_ops() {
2055 let mut g = Gte::new();
2058 g.write_data(8, 0x1000); let instr = cmd_word(true, false, 0, 0, 0, 0x3D);
2060 g.write_data(9, 0x100);
2061 g.write_data(10, 0x200);
2062 g.write_data(11, 0x300);
2063 g.execute(instr);
2064 g.write_data(9, 0x400);
2065 g.write_data(10, 0x500);
2066 g.write_data(11, 0x600);
2067 g.execute(instr);
2068 g.write_data(9, 0x700);
2069 g.write_data(10, 0x100);
2070 g.write_data(11, 0x200);
2071 g.execute(instr);
2072 assert_eq!(g.read_data(20), 0x0030_2010, "oldest push");
2075 assert_eq!(g.read_data(21), 0x0060_5040, "middle push");
2076 assert_eq!(g.read_data(22), 0x0020_1070, "newest push");
2077 }
2078
2079 #[test]
2080 fn sqr_sf1_scales_product_down_by_4096() {
2081 let mut g = Gte::new();
2082 g.write_data(9, 0x1000);
2083 g.write_data(10, 0x0800);
2084 g.write_data(11, 0x2000);
2085 g.execute(cmd_word(true, false, 0, 0, 0, 0x28));
2086 assert_eq!(g.read_data(25) as i32, 0x1000);
2088 assert_eq!(g.read_data(26) as i32, 0x400);
2089 assert_eq!(g.read_data(27) as i32, 0x4000);
2090 }
2091
2092 #[test]
2093 fn rtps_h_equals_2sz3_sets_divide_overflow() {
2094 let mut g = Gte::new();
2097 install_identity_rotation(&mut g);
2098 g.write_data(0, pack_xy_i16(0, 0));
2099 g.write_data(1, 1); g.write_control(26, 2); g.execute(cmd_word(true, false, 0, 0, 0, 0x01));
2102 assert!(g.read_control(31) & flag::DIV_OVERFLOW != 0);
2103 }
2104
2105 #[test]
2106 fn rtps_unr_table_index_256_is_not_aliased() {
2107 let mut g = Gte::new();
2118 install_identity_rotation(&mut g);
2119 g.write_data(0, pack_xy_i16(0x100, 0));
2123 g.write_data(1, 0x7FFF);
2124 g.write_control(24, 0); g.write_control(25, 0); g.write_control(26, 0x4000); g.execute(cmd_word(true, false, 0, 0, 0, 0x01));
2128 assert_eq!(g.read_data(19), 0x7FFF, "SZ3 fixture");
2129 let sxy2 = g.read_data(14);
2133 let sx2 = (sxy2 & 0xFFFF) as i16;
2134 assert_eq!(sx2, 0x80, "SX2 should be 0x80, was {sx2:#x}");
2135 }
2136
2137 #[test]
2138 fn unknown_opcode_leaves_all_registers_clear() {
2139 let mut g = Gte::new();
2142 g.execute(0x3C); for i in 0..31 {
2144 assert_eq!(g.read_control(i), 0, "ctrl {i}");
2145 }
2146 assert_eq!(g.read_control(31), 0, "flag");
2147 }
2148
2149 #[test]
2150 fn mvmva_cv2_fc_bug_drops_translation_and_first_column() {
2151 let mut g = Gte::new();
2158 install_identity_rotation(&mut g);
2159 g.write_control(21, 0x1000_0000); g.write_data(0, pack_xy_i16(1, 0)); g.write_data(1, 0);
2162 g.execute(cmd_word(true, false, 0, 0, 2, 0x12));
2163 assert_eq!(g.read_data(25) as i32, 0, "MAC1: column 0 (Vx) dropped");
2166 assert_eq!(g.read_data(9), 0, "IR1: column 0 (Vx) dropped");
2167 assert!(
2169 g.read_control(31) & flag::IR1_SAT != 0,
2170 "IR1_SAT from discarded first term"
2171 );
2172 }
2173
2174 #[test]
2175 fn mvmva_mx3_uses_garbage_matrix() {
2176 let mut g = Gte::new();
2181 g.write_data(6, 0x0000_0010); g.write_data(8, 0x100); g.write_control(1, pack_xy_i16(0x200, 0)); g.write_control(2, pack_xy_i16(0x300, 0)); g.write_data(0, pack_xy_i16(1, 1));
2186 g.write_data(1, 1); g.execute(cmd_word(false, false, 3, 0, 3, 0x12));
2188 assert_eq!(g.read_data(25) as i32, 0x100, "MAC1");
2195 assert_eq!(g.read_data(26) as i32, 0x600, "MAC2");
2196 assert_eq!(g.read_data(27) as i32, 0x900, "MAC3");
2197 }
2198
2199 #[test]
2200 fn mvmva_vx3_uses_ir_as_vector() {
2201 let mut g = Gte::new();
2204 install_identity_rotation(&mut g);
2205 g.write_data(9, 5);
2206 g.write_data(10, 6);
2207 g.write_data(11, 7);
2208 g.execute(cmd_word(true, false, 0, 3, 3, 0x12));
2210 assert_eq!(g.read_data(25) as i32, 5);
2211 assert_eq!(g.read_data(26) as i32, 6);
2212 assert_eq!(g.read_data(27) as i32, 7);
2213 }
2214}