From 71993003aebfa6165344660b46deb35943957306 Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Wed, 19 Nov 2025 12:03:35 +1300 Subject: [PATCH 1/8] psx/hw/cop: Use .long to form unsupported cop2 opcodes Very rough code to test that the approach works. closes #7 --- psx/src/hw/cop.rs | 12 +++++------- psx/src/hw/gte.rs | 38 +++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/psx/src/hw/cop.rs b/psx/src/hw/cop.rs index 3693d358..a3253c75 100644 --- a/psx/src/hw/cop.rs +++ b/psx/src/hw/cop.rs @@ -21,7 +21,7 @@ impl AsMut for CopRegister; COP: $cop:expr; R: $reg:expr $(,)?) => { - define_cop!($(#[$($meta)*])* $name<$ty>; COP: $cop; R: $reg; "m"); + define_cop!($(#[$($meta)*])* $name<$ty>; COP: $cop; R: $reg; "0"); }; ($(#[$($meta:meta)*])* $name:ident <$ty:ty>; COP: $cop:expr; R: $reg:expr; $cop_ty:literal $(,)?) => { $(#[$($meta)*])* @@ -34,9 +34,8 @@ macro_rules! define_cop { fn load(&mut self) -> &mut Self { unsafe { core::arch::asm! { - ".set noat", - concat!($cop_ty, "fc", $cop, " {}, $", $reg), - ".set at", + //concat!($cop_ty, "fc", $cop, " {}, $", $reg), + concat!(".long 1<<30 | ", $cop, "<<26 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11 # {}"), out(reg) self.value, options(nomem, nostack) } @@ -47,9 +46,8 @@ macro_rules! define_cop { fn store(&mut self) -> &mut Self { unsafe { core::arch::asm! { - ".set noat", - concat!($cop_ty, "tc", $cop, " {}, $", $reg), - ".set at", + //concat!($cop_ty, "tc", $cop, " {}, $", $reg), + concat!(".long 1<<30 | ", $cop, "<<26 | 1<<23 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11 # {}"), in(reg) self.value, options(nomem, nostack) } diff --git a/psx/src/hw/gte.rs b/psx/src/hw/gte.rs index f39d9a47..6b4ab2c9 100644 --- a/psx/src/hw/gte.rs +++ b/psx/src/hw/gte.rs @@ -36,39 +36,43 @@ define_cop! { /// Leading zeros count result LZCR; COP: 2; R: 31, - /* - TODO: LLVM doesn't support these coprocessor instructions yet (#7). + // TODO: LLVM doesn't support these coprocessor instructions yet (#7). /// Rotation matrix entries RT11 and RT12 RT11_12; COP: 2; R: 32; "c", + /// Rotation matrix entries RT13 and RT21 - RT13_21; COP: 2; R: 33, + RT13_21; COP: 2; R: 33; "c", /// Rotation matrix entries RT22 and RT23 - RT22_23; COP: 2; R: 34, + RT22_23; COP: 2; R: 34; "c", /// Rotation matrix entries RT31 and RT32 - RT31_32; COP: 2; R: 35, + RT31_32; COP: 2; R: 35; "c", /// Rotation matrix entry RT33 - RT33; COP: 2; R: 36, + RT33; COP: 2; R: 36; "c", /// Light matrix entries L11 and L12 - L11_12; COP: 2; R: 40, + L11_12; COP: 2; R: 40; "c", /// Light matrix entries L13 and L21 - L13_21; COP: 2; R: 41, + L13_21; COP: 2; R: 41; "c", /// Light matrix entries L22 and L23 - L22_23; COP: 2; R: 42, + L22_23; COP: 2; R: 42; "c", /// Light matrix entries L31 and L32 - L31_32; COP: 2; R: 43, + L31_32; COP: 2; R: 43; "c", /// Light matrix entry L33 - L33; COP: 2; R: 44, + L33; COP: 2; R: 44; "c", /// Light color matrix entries LR11 and LR12 - LR11_12; COP: 2; R: 48, + LR11_12; COP: 2; R: 48; "c", /// Light color matrix entries LR13 and LR21 - LR13_21; COP: 2; R: 49, + LR13_21; COP: 2; R: 49; "c", /// Light color matrix entries LR22 and LR23 - LR22_23; COP: 2; R: 50, + LR22_23; COP: 2; R: 50; "c", /// Light color matrix entries LR31 and LR32 - LR31_32; COP: 2; R: 51, + LR31_32; COP: 2; R: 51; "c", /// Light color matrix entry LR33 - LR33; COP: 2; R: 52, - */ + LR33; COP: 2; R: 52; "c", + + /// Screen Offset and Distance X + OFX; COP: 2; R: 56; "c", + /// Screen Offset and Distance Y + OFY; COP: 2; R: 57; "c", } From 84fa88e798703332a25ccddf9ed50b45666fce37 Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Wed, 19 Nov 2025 16:11:08 +1300 Subject: [PATCH 2/8] Use $at to hold the value from asm! macro reg so we can hardcode rt=$at in the cop opcode --- psx/src/hw/cop.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/psx/src/hw/cop.rs b/psx/src/hw/cop.rs index a3253c75..62c29a24 100644 --- a/psx/src/hw/cop.rs +++ b/psx/src/hw/cop.rs @@ -35,7 +35,10 @@ macro_rules! define_cop { unsafe { core::arch::asm! { //concat!($cop_ty, "fc", $cop, " {}, $", $reg), - concat!(".long 1<<30 | ", $cop, "<<26 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11 # {}"), + ".set noat", + concat!(".long 1<<30 | ", $cop, "<<26 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11"), + "addiu {}, $at, 0", + ".set at", out(reg) self.value, options(nomem, nostack) } @@ -47,7 +50,10 @@ macro_rules! define_cop { unsafe { core::arch::asm! { //concat!($cop_ty, "tc", $cop, " {}, $", $reg), - concat!(".long 1<<30 | ", $cop, "<<26 | 1<<23 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11 # {}"), + ".set noat", + "addiu $at, {}, 0", + concat!(".long 1<<30 | ", $cop, "<<26 | 1<<23 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11"), + ".set at", in(reg) self.value, options(nomem, nostack) } From 1ab40edb54c502b4f4afd60889a43ab59166d33a Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Wed, 19 Nov 2025 17:42:46 +1300 Subject: [PATCH 3/8] psx/hw/gte: Add two more cop2 registers, nop delay for read from cop --- psx/src/hw/cop.rs | 1 + psx/src/hw/gte.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/psx/src/hw/cop.rs b/psx/src/hw/cop.rs index 62c29a24..b1ce4c17 100644 --- a/psx/src/hw/cop.rs +++ b/psx/src/hw/cop.rs @@ -37,6 +37,7 @@ macro_rules! define_cop { //concat!($cop_ty, "fc", $cop, " {}, $", $reg), ".set noat", concat!(".long 1<<30 | ", $cop, "<<26 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11"), + "nop", "addiu {}, $at, 0", ".set at", out(reg) self.value, diff --git a/psx/src/hw/gte.rs b/psx/src/hw/gte.rs index 6b4ab2c9..48f84044 100644 --- a/psx/src/hw/gte.rs +++ b/psx/src/hw/gte.rs @@ -71,8 +71,14 @@ define_cop! { /// Light color matrix entry LR33 LR33; COP: 2; R: 52; "c", + /// Screen Offset and Distance X OFX; COP: 2; R: 56; "c", /// Screen Offset and Distance Y OFY; COP: 2; R: 57; "c", + /// Projection plane distance + H; COP: 2; R: 58; "c", + + /// Error code status + FLAG; COP: 2; R: 63; "c", } From 1c0507e6a5f62ccbb0edfd8c8f2d7ac86069ffba Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Wed, 19 Nov 2025 21:45:08 +1300 Subject: [PATCH 4/8] psx/hw/gte: Define intermediate GTE registers --- psx/src/hw/gte.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/psx/src/hw/gte.rs b/psx/src/hw/gte.rs index 48f84044..c261a0e8 100644 --- a/psx/src/hw/gte.rs +++ b/psx/src/hw/gte.rs @@ -21,6 +21,15 @@ define_cop! { /// Ordering table average Z value OTZ; COP: 2; R: 7, + /// Intermediate value 0 + IR0; COP: 2; R: 8, + /// Intermediate value 1 + IR1; COP: 2; R: 9, + /// Intermediate value 2 + IR2; COP: 2; R: 10, + /// Intermediate value 3 + IR3; COP: 2; R: 11, + /// Scalar math accumulator MAC0; COP: 2; R: 24, From a0d06c282db4d689665ee59db4e7a1f55d8748e3 Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Thu, 20 Nov 2025 14:31:13 +1300 Subject: [PATCH 5/8] psx/hw/cop: add cop_move! asm generating macro --- psx/src/hw/cop.rs | 44 ++++++++++++++++++++++++++++++++++---------- psx/src/hw/gte.rs | 1 - 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/psx/src/hw/cop.rs b/psx/src/hw/cop.rs index b1ce4c17..1b8a2810 100644 --- a/psx/src/hw/cop.rs +++ b/psx/src/hw/cop.rs @@ -19,11 +19,40 @@ impl AsMut for CopRegister { + concat!( + "mfc", $cop, " {}, $", $reg, "\n", + "nop" // required to ensure value is in out(reg) + ) + }; + ("m", to, $cop:expr, $reg:expr) => { + concat!("mtc", $cop, " {}, $", $reg) + }; + ("c", from, $cop:expr, $reg:expr) => { + //concat!("cfc", $cop, " {}, $", $reg) # Not currently supported by LLVM + concat!( + // cop n CF rt=$at rd=$reg - 32 + ".long 1<<30 | ", $cop, "<<26 | 2<<21 | 1<<16 | (", $reg, "-32)<<11 # cfc", $cop, "\n", + "nop\n", + "addiu {}, $at, 0" + ) + }; + ("c", to, $cop:expr, $reg:expr) => { + //concat!("ctc", $cop, " {}, $", $reg) # Not currently supported by LLVM + concat!( + "addiu $at, {}, 0\n", + // cop n CT rt=$at rd=$reg - 32 + ".long 1<<30 | ", $cop, "<<26 | 6<<21 | 1<<16 | (", $reg, "-32)<<11 # ctc", $cop + ) + }; +} + macro_rules! define_cop { ($(#[$($meta:meta)*])* $name:ident <$ty:ty>; COP: $cop:expr; R: $reg:expr $(,)?) => { - define_cop!($(#[$($meta)*])* $name<$ty>; COP: $cop; R: $reg; "0"); + define_cop!($(#[$($meta)*])* $name<$ty>; COP: $cop; R: $reg; "m"); }; - ($(#[$($meta:meta)*])* $name:ident <$ty:ty>; COP: $cop:expr; R: $reg:expr; $cop_ty:literal $(,)?) => { + ($(#[$($meta:meta)*])* $name:ident <$ty:ty>; COP: $cop:expr; R: $reg:expr; $cop_ty:tt $(,)?) => { $(#[$($meta)*])* pub type $name = crate::hw::cop::CopRegister<$ty, $cop, $reg>; @@ -34,11 +63,8 @@ macro_rules! define_cop { fn load(&mut self) -> &mut Self { unsafe { core::arch::asm! { - //concat!($cop_ty, "fc", $cop, " {}, $", $reg), ".set noat", - concat!(".long 1<<30 | ", $cop, "<<26 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11"), - "nop", - "addiu {}, $at, 0", + cop_move!($cop_ty, from, $cop, $reg), ".set at", out(reg) self.value, options(nomem, nostack) @@ -50,10 +76,8 @@ macro_rules! define_cop { fn store(&mut self) -> &mut Self { unsafe { core::arch::asm! { - //concat!($cop_ty, "tc", $cop, " {}, $", $reg), ".set noat", - "addiu $at, {}, 0", - concat!(".long 1<<30 | ", $cop, "<<26 | 1<<23 | (0x", $cop_ty, "/6)<<21 | 1 << 16 | (", $reg, " - 0x", $cop_ty, "/12*32) << 11"), + cop_move!($cop_ty, to, $cop, $reg), ".set at", in(reg) self.value, options(nomem, nostack) @@ -63,7 +87,7 @@ macro_rules! define_cop { } } }; - ($(#[$($meta:meta)*])* $name:ident <$ty:ty>; COP: $cop:expr; R: $reg:expr $(;$cop_ty:literal)?, $($others:tt)*) => { + ($(#[$($meta:meta)*])* $name:ident <$ty:ty>; COP: $cop:expr; R: $reg:expr $(;$cop_ty:tt)?, $($others:tt)*) => { define_cop!($(#[$($meta)*])* $name<$ty>; COP: $cop; R: $reg $(;$cop_ty)*); define_cop!($($others)*); }; diff --git a/psx/src/hw/gte.rs b/psx/src/hw/gte.rs index c261a0e8..b38d3893 100644 --- a/psx/src/hw/gte.rs +++ b/psx/src/hw/gte.rs @@ -45,7 +45,6 @@ define_cop! { /// Leading zeros count result LZCR; COP: 2; R: 31, - // TODO: LLVM doesn't support these coprocessor instructions yet (#7). /// Rotation matrix entries RT11 and RT12 RT11_12; COP: 2; R: 32; "c", From ff2d012537a25dbc6d61d55e86ec674c68c8691f Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Fri, 21 Nov 2025 21:43:28 +1300 Subject: [PATCH 6/8] psx/hw/gte: Define remaining GTE registers --- psx/src/hw/gte.rs | 74 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/psx/src/hw/gte.rs b/psx/src/hw/gte.rs index b38d3893..370911a4 100644 --- a/psx/src/hw/gte.rs +++ b/psx/src/hw/gte.rs @@ -17,18 +17,47 @@ define_cop! { VXY2; COP: 2; R: 4, /// The 16-bit VZ2 vector VZ2; COP: 2; R: 5, - + /// RGB value + RGB; COP: 2; R: 6, /// Ordering table average Z value OTZ; COP: 2; R: 7, /// Intermediate value 0 - IR0; COP: 2; R: 8, + IR0; COP: 2; R: 8, /// Intermediate value 1 - IR1; COP: 2; R: 9, + IR1; COP: 2; R: 9, /// Intermediate value 2 - IR2; COP: 2; R: 10, + IR2; COP: 2; R: 10, /// Intermediate value 3 - IR3; COP: 2; R: 11, + IR3; COP: 2; R: 11, + + /// Screen XY coord FIFO 0 + SXY0; COP: 2; R: 12, + /// Screen XY coord FIFO 1 + SXY1; COP: 2; R: 13, + /// Screen XY coord FIFO 2 + SXY2; COP: 2; R: 14, + /// Screen XY coord FIFO P + SXYP; COP: 2; R: 15, + + /// Screen Z FIFO 0 + SZ0; COP: 2; R: 16, + /// Screen Z FIFO 1 + SZ1; COP: 2; R: 17, + /// Screen Z FIFO 2 + SZ2; COP: 2; R: 18, + /// Screen Z FIFO 3 + SZ3; COP: 2; R: 19, + + /// Characteristic color FIFO 0 + RGB0; COP: 2; R: 20, + /// Characteristic color FIFO 1 + RGB1; COP: 2; R: 21, + /// Characteristic color FIFO 2 + RGB2; COP: 2; R: 22, + + /// Unused / Prohibited + RES1; COP: 2; R: 23, /// Scalar math accumulator MAC0; COP: 2; R: 24, @@ -40,6 +69,11 @@ define_cop! { /// The third component of the vector math accumulator MAC3; COP: 2; R: 27, + /// IRGB + IRGB; COP: 2; R: 28, + /// ORGB + ORGB; COP: 2; R: 29, + /// Leading zeros count source LZCS; COP: 2; R: 30, /// Leading zeros count result @@ -57,6 +91,13 @@ define_cop! { /// Rotation matrix entry RT33 RT33; COP: 2; R: 36; "c", + /// Translation vector X + TRX; COP: 2; R: 37; "c", + /// Translation vector Y + TRY; COP: 2; R: 38; "c", + /// Translation vector Z + TRZ; COP: 2; R: 39; "c", + /// Light matrix entries L11 and L12 L11_12; COP: 2; R: 40; "c", /// Light matrix entries L13 and L21 @@ -68,6 +109,13 @@ define_cop! { /// Light matrix entry L33 L33; COP: 2; R: 44; "c", + /// Background color red component + RBK; COP: 2; R: 45; "c", + /// Background color green component + GBK; COP: 2; R: 46; "c", + /// Background color blue component + BBK; COP: 2; R: 47; "c", + /// Light color matrix entries LR11 and LR12 LR11_12; COP: 2; R: 48; "c", /// Light color matrix entries LR13 and LR21 @@ -79,6 +127,12 @@ define_cop! { /// Light color matrix entry LR33 LR33; COP: 2; R: 52; "c", + /// Far color red component + RFC; COP: 2; R: 53; "c", + /// Far color green component + GFC; COP: 2; R: 54; "c", + /// Far color blue component + BFC; COP: 2; R: 55; "c", /// Screen Offset and Distance X OFX; COP: 2; R: 56; "c", @@ -87,6 +141,16 @@ define_cop! { /// Projection plane distance H; COP: 2; R: 58; "c", + /// Depth queuing parameter A. (coefficient) + DQA; COP: 2; R: 59; "c", + /// Depth queuing parameter B. (offset) + DQB; COP: 2; R: 60; "c", + + /// Z3 average scale factor (normally 1/3) + ZSF3; COP: 2; R: 61; "c", + /// Z4 average scale factor (normally 1/4) + ZSF3; COP: 2; R: 62; "c", + /// Error code status FLAG; COP: 2; R: 63; "c", } From eb3d1072b2e969c1fd7cf893dae1a09b3ae72114 Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Fri, 21 Nov 2025 21:45:46 +1300 Subject: [PATCH 7/8] Fix typo in register name --- psx/src/hw/gte.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psx/src/hw/gte.rs b/psx/src/hw/gte.rs index 370911a4..048379a0 100644 --- a/psx/src/hw/gte.rs +++ b/psx/src/hw/gte.rs @@ -149,7 +149,7 @@ define_cop! { /// Z3 average scale factor (normally 1/3) ZSF3; COP: 2; R: 61; "c", /// Z4 average scale factor (normally 1/4) - ZSF3; COP: 2; R: 62; "c", + ZSF4; COP: 2; R: 62; "c", /// Error code status FLAG; COP: 2; R: 63; "c", From df4c70834b3521a231217be20a3a47ec2b7203f3 Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Tue, 25 Nov 2025 20:47:20 +1300 Subject: [PATCH 8/8] psx/hw/cop: 2 delay slots for GTE load and store Source: Hitmen psx docs https://hitmen.c02.at/files/docs/psx/psx.pdf p. 51 > GTE load and store instructions have a delay of 2 instructions, > for any GTE commands or operations accessing that > register. and local testing which showed sporadic errors in some examples. Sometimes the delays seem to occur naturally, so this was hard to verify. 2 nops after GTE opcodes and constructed opcodes seems to prevent all errors reliably. --- psx/src/hw/cop.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/psx/src/hw/cop.rs b/psx/src/hw/cop.rs index 1b8a2810..3674fb95 100644 --- a/psx/src/hw/cop.rs +++ b/psx/src/hw/cop.rs @@ -23,11 +23,15 @@ macro_rules! cop_move { ("m", from, $cop:expr, $reg:expr) => { concat!( "mfc", $cop, " {}, $", $reg, "\n", - "nop" // required to ensure value is in out(reg) + "nop\n", + "nop", // 2 delay slots required to ensure value is in out(reg) ) }; ("m", to, $cop:expr, $reg:expr) => { - concat!("mtc", $cop, " {}, $", $reg) + concat!("mtc", $cop, " {}, $", $reg, "\n", + "nop\n", + "nop" + ) }; ("c", from, $cop:expr, $reg:expr) => { //concat!("cfc", $cop, " {}, $", $reg) # Not currently supported by LLVM @@ -35,6 +39,7 @@ macro_rules! cop_move { // cop n CF rt=$at rd=$reg - 32 ".long 1<<30 | ", $cop, "<<26 | 2<<21 | 1<<16 | (", $reg, "-32)<<11 # cfc", $cop, "\n", "nop\n", + "nop\n", "addiu {}, $at, 0" ) }; @@ -43,7 +48,9 @@ macro_rules! cop_move { concat!( "addiu $at, {}, 0\n", // cop n CT rt=$at rd=$reg - 32 - ".long 1<<30 | ", $cop, "<<26 | 6<<21 | 1<<16 | (", $reg, "-32)<<11 # ctc", $cop + ".long 1<<30 | ", $cop, "<<26 | 6<<21 | 1<<16 | (", $reg, "-32)<<11 # ctc", $cop, "\n", + "nop\n", + "nop", ) }; }