From fe93f26e3ce148809033d27882fe029bb3696131 Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Tue, 25 Nov 2025 18:42:36 +1300 Subject: [PATCH] texture warp example, 4/8bit TIMs and GTE testing WIP --- A.3_TextureWarp/.cargo/config.toml | 2 + A.3_TextureWarp/Cargo.toml | 8 ++ A.3_TextureWarp/crate4bit.tim | Bin 0 -> 2112 bytes A.3_TextureWarp/crate8bit.tim | Bin 0 -> 4640 bytes A.3_TextureWarp/src/main.rs | 157 +++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+) create mode 100644 A.3_TextureWarp/.cargo/config.toml create mode 100644 A.3_TextureWarp/Cargo.toml create mode 100644 A.3_TextureWarp/crate4bit.tim create mode 100644 A.3_TextureWarp/crate8bit.tim create mode 100644 A.3_TextureWarp/src/main.rs diff --git a/A.3_TextureWarp/.cargo/config.toml b/A.3_TextureWarp/.cargo/config.toml new file mode 100644 index 0000000..8d98ba9 --- /dev/null +++ b/A.3_TextureWarp/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.mipsel-sony-psx] +runner = "mednafen" diff --git a/A.3_TextureWarp/Cargo.toml b/A.3_TextureWarp/Cargo.toml new file mode 100644 index 0000000..6a1af90 --- /dev/null +++ b/A.3_TextureWarp/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "texturewarp" +version = "0.1.0" +edition = "2021" + +[dependencies] +#psx = { git = "https://github.com/ayrtonm/psx-sdk-rs.git" } +psx = { path = "../../psx-sdk-rs/psx" } diff --git a/A.3_TextureWarp/crate4bit.tim b/A.3_TextureWarp/crate4bit.tim new file mode 100644 index 0000000000000000000000000000000000000000..467d15f4fca7b1b6d9e1fe3556e0532877189173 GIT binary patch literal 2112 zcmXX{PiPz28GqIkN3>gz9B)(iB)_ROE^OG@dG$zLSZL{JgXL0C;&Cda2U}ym8KpJ2 z(t2hRFc=$Vo@nQ=-dNTXDup74XzXJz@dONIVe3Rt*g~R<4?XV1x%ARHc>2lhCq{?) zegEhCeqRb9l!WI4co6#MSPG4y)#Tc@ywqXpS@K!xtI5AjetY^mQ}4b1Vfw?V-`-yQ z?q^f)fx&h{Mk(}Dw8iW!A-ImKhM&X8b%-Q3hUe#u4TlyW>OJM!b6p~aW3y2x*~>eO z`4&^LjAU%8Sy7fn+m^(tts>$B#zsQjT*I-1geVCz6+}Oed}=M9gKU8r!gc1FWFmvp zGQtRv1hyGFKMx2Q1r#8TO&rs@C)TqF-U&A+#=P?YkE5Go0Lyh|6Hw9?bzRdIH>vr( zQhH-3foW>`BSJ``aqURcilG*UM`75}6vcNe{9>Yd z%WQFwlOx^_dB3k~V`)t7MNONj(ogjo<-82P{(Kld;bDhs1(HDGf_ZS(1*^e}-yBfJ z-}%;z$Fj;&<(86x@=VH)SD{M8!UT)PDCTes&ftlZHQEGu#n6?nV$wAmiKi{tndd}XV)QB@ngA|kS)B+;bL+;+%0;QSA6tA=gr zG3p!^QASpllX9^?2s`1+&Oywnxx{Cxj^i`i(uyL=+%S@~`1a!B;`EsFGsp7w%ZhDc z&-Kh+MI;$)sBSU3j4!U@sMhBv5BLnVRf9QZPJ1AXXK=&&)dDoPABItM5?$_>R~!Dn)-k^X(bx`5O#V^A$_GfpPE1>GL&eMmYN%GDg_W{4UdKCyF*Ama2)g|pYr%O@kKmd zXO@&4u77?ML-b#Vi^ec8q)Z?zw|NW&Wp@IabbRn1b!~cB-1N`$4YU&o8@vYV(Wb8h z8*poH;gM?G54k_Igx$oBi@xA(&fJ4X8|M}?cLAq9@9`#MS=pydk@@pigX6(qDl=3! zM8J!F^G*j`Qe3ya02yT%ecIb(ZbP)Q-f&%R4PcyMnri6vC$i$_pA_lhW6z>aRYD$fDQsE)W%%NFA8mgk zH~iVs5ANuM*cf9{%~5g?|9!NqyvdQgwV~|JmTKA@TDC>S@Ng<)d^Lbvm`N!YWDA3p%63uw9qh4*&jGpl*4VR#}sAf*?^QoPT-TWTV_50iH#HyY5-53?83F2cEg{b-C@4rtTfLh5i36r3*G+z z!NCA}*vZQ~EMKR;V}Yq6vrzer@rXYMj(}YVYqXOW0C(sLbKyAL)e3NRSR24T497`y z@?V^fag$a~U{R zY{;6XLrrRmRrsSe-ON_0soYzusxo8N^>G8GkywnHHuXny;zR2ZQVc5Ls&jpJuQizC zI8qf9CV(p~j1okwn~v@I4rw@|LK{S`n>pZlX&5k7kb$Vh1f+?aENO_2iB*~SmZ+k0 c-!c^#Uvx=<>iV)MN(O}XBzkmevGH4Sg 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 &mut PolyF4; + fn as_text(&mut self) -> &mut PolyFT4; +} +impl SafeUnionAccess for Packet { // taken from ayrtonm/psx-sdk-rs/tree/main/examples/monkey/src/main.rs + fn as_flat(&mut self) -> &mut PolyF4 { + // SAFETY: We resize the packet to hold a PolyF4 and reset the polygon's command + // to ensure that the union's PolyF4 is in a valid state when we access it + unsafe { self.resize::().contents.flat.reset_cmd() } + } + fn as_text(&mut self) -> &mut PolyFT4 { + unsafe { self.resize::().contents.text.reset_cmd() } + } +} + +impl GP0Command for PolyF {} + + +#[no_mangle] +fn main() { + let mut fb = Framebuffer::new((0, 0), (0, 240), (320, 240), VideoMode::NTSC, Some(Color::new(70,70,70))).unwrap(); + let texture_tim = include_tim!("../crate4bit.tim"); + let bpp = texture_tim.bpp; + let clt = texture_tim.clut.size; + let offset = texture_tim.bmp.offset; + + let mut gpu_dma = dma::GPU::new(); + + let mut db = 0; // display buffer 0 or 1 + + // Set up 2 x ordering tables in a 2x8 array + // using the multi-primitive approach suggested by psx-sdk-rs monkey example + // .. this is still not a primitive buffer tho + let mut ot = [const { Packet::new(PolyF { flat: PolyF4::new() }) }; 16]; + + link_list(&mut ot[0..8]); + link_list(&mut ot[8..16]); + let loaded_font = fb.load_default_font(); + let mut txt = loaded_font.new_text_box((0, 8), (100, 50)); + + let loaded_tim = fb.load_tim(texture_tim); + + // Location and Dimensions of the square + let (x, y) = (132, 132); + let (h, w) = (64, 64); + // Location of the sprite + let (sx, sy) = (148, 148); + // Texture coordinates for the sprite + let tex_coords = [(0, 0 + 48), (0, 64 + 48), (64, 0+48), (64, 64+48)].map(|(x, y)| TexCoord { x, y }); + + let mut status = cop0::Status::new(); + status.assign(ENABLE_GTE).store(); + let otz = OTZ::new(); + let mut hpos = H::new(); + hpos.assign(123).store(); + let cop2r56 = OFX::new(); + + let cop2r1 = VZ0::new(); + + let cop2r57 = OFY::new(); + let cop2r0 = VXY0::new(); + hpos.assign(200); // assign(), but don't .store() + let cop2r5 = VZ2::new(); + + let pos = H::new(); + + // SQR, set up input vector + let mut ir1 = IR1::new(); + ir1.assign(1).store(); + let mut ir2 = IR2::new(); + ir2.assign(3).store(); + let mut ir3 = IR3::new(); + ir3.assign(5).store(); + + // SQR send cop2 + unsafe { + core::arch::asm! { + "nop", + ".long {} & 0x1ffffff | 37 << 25 # cop2", + const SQR_OP, + options(nomem, nostack) + } + } + + // re-read ir1,2,3 for output vector + let out_ir1 = IR1::new(); + let out_ir2 = IR2::new(); + let out_ir3 = IR3::new(); + + let flag = FLAG::new(); + + // Main loop + loop { + let (a, b) = ot.split_at_mut(8); + let (display, draw) = if db == 1 { (a, b) } else { (b, a) }; + dprintln!(txt, "CLUT size:{:?}", clt); + dprintln!(txt, "GTE: {:?}", status.gte_enabled()); + dprintln!(txt, "OFX: {}", cop2r56.to_bits()); + dprintln!(txt, "VZ0: {}", cop2r1.to_bits()); + dprintln!(txt, "OFY: {}", cop2r57.to_bits()); + dprintln!(txt, "VXY0: {}", cop2r0.to_bits()); + dprintln!(txt, "VZ2: {}", cop2r5.to_bits()); + dprintln!(txt, "HPOS: {}", hpos.to_bits()); + dprintln!(txt, "POS: {}", pos.to_bits()); + + dprintln!(txt, "OTZ: {}", otz.to_bits()); + dprintln!(txt, "SQR In X,Y,Z: <{},{},{}>", ir1.to_bits(), ir2.to_bits(), ir3.to_bits()); + dprintln!(txt, "SQR Out X,Y,Z: <{},{},{}>", out_ir1.to_bits(), out_ir2.to_bits(), out_ir3.to_bits()); + dprintln!(txt, "FLAG: {:b}", flag.to_bits()); + txt.reset(); + gpu_dma.send_list_and(display, || { + draw[1] + // TODO: be clear about Vertex and TexCoord ordering! + .as_text().set_vertices([(sx, sy), (sx, sy+h), (sx+w, sy), (sx+w, sy+h)].map(|v| Vertex::new(v))) + .set_color(Color::new(255, 255, 255)) + .set_tex_page(loaded_tim.tex_page) + .set_tex_coords(tex_coords) + .set_clut(loaded_tim.clut.unwrap()); + draw[0] + .as_flat().set_vertices([Vertex(x, y), Vertex(x+w, y), Vertex(x, y+h), Vertex(x+w, y+h)]) + .set_color(Color::new(23, 24, 78)); + }); + + // Wait for GPU to finish drawing and V-Blank + fb.draw_sync(); + fb.wait_vblank(); + + // Flip buffers and display + fb.dma_swap(&mut gpu_dma); + // switch display / draw ot lists + db = 1 - db; + } +}