From fe93f26e3ce148809033d27882fe029bb3696131 Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Tue, 25 Nov 2025 18:42:36 +1300 Subject: [PATCH 1/2] 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; + } +} From 34f58ec480fcc5473cbea937990a3c9ac32eafff Mon Sep 17 00:00:00 2001 From: Charles Horn Date: Wed, 3 Dec 2025 17:32:49 +1300 Subject: [PATCH 2/2] testing 3D primative drawing, following primdraw.c --- 1.7_Primdraw3D/.cargo/config.toml | 2 + 1.7_Primdraw3D/Cargo.toml | 8 + 1.7_Primdraw3D/src/main.rs | 343 ++++++++++++++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 1.7_Primdraw3D/.cargo/config.toml create mode 100644 1.7_Primdraw3D/Cargo.toml create mode 100644 1.7_Primdraw3D/src/main.rs diff --git a/1.7_Primdraw3D/.cargo/config.toml b/1.7_Primdraw3D/.cargo/config.toml new file mode 100644 index 0000000..8d98ba9 --- /dev/null +++ b/1.7_Primdraw3D/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.mipsel-sony-psx] +runner = "mednafen" diff --git a/1.7_Primdraw3D/Cargo.toml b/1.7_Primdraw3D/Cargo.toml new file mode 100644 index 0000000..dc1d163 --- /dev/null +++ b/1.7_Primdraw3D/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Primdraw" +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/1.7_Primdraw3D/src/main.rs b/1.7_Primdraw3D/src/main.rs new file mode 100644 index 0000000..502fca7 --- /dev/null +++ b/1.7_Primdraw3D/src/main.rs @@ -0,0 +1,343 @@ +#![no_std] +#![no_main] +#![feature(asm_experimental_arch)] + +use psx::{dma, dprintln, Framebuffer}; +use psx::constants::*; +use psx::gpu::primitives::{PolyF3}; +use psx::gpu::{Color, link_list, Packet, TexCoord, Vertex, VideoMode}; +use psx::hw::{cop0, gte, Register}; +use psx::math::{sin, cos, Rad}; +use psx::sys::gamepad::{Gamepad, Button}; + +const NTSC: bool = true; // toggle between NTSC and PAL modes and texture + // +// Attempt to convert Lameguy64 / Meido-Tek primdraw.c 3D example from C to Rust.. + +/* primdraw.c by Lameguy64 + 2014 Meido-Tek Productions. + + Demonstrates: + - Using a primitive OT to draw triangles without libgs. + - Using the GTE to rotate, translate, and project 3D primitives. + + Controls: + Start - Toggle interactive/non-interactive mode. + Select - Reset object's position and angles. + L1/L2 - Move object closer/farther. + L2/R2 - Rotate object (XY). + Up/Down/Left/Right - Rotate object (XZ/YZ). + Triangle/Cross/Square/Circle - Move object up/down/left/right. + +*/ + +pub struct Trans { + pub x: i16, + pub y: i16, + pub z: i16, +} + +pub struct Rotate { + pub x: i16, + pub y: i16, + pub z: i16, +} + +const CENTERX: i16 = 320; +const CENTERY: i16 = 120; +const OT_LENGTH: usize = 48; // breaks at 64, 50? works at 32, 40, 48, 49 +const ENABLE_GTE: u32 = 1 << 30; +const RTPS: u32 = 0x0180001; +const ONE: i16 = 1<<12; + + +#[inline(always)] +pub fn delay() { + unsafe { + core::arch::asm!( + "nop", + // "nop", + ); + } +} + +/// Convert Euler rotation vector into fixed point rotation matrix +fn rot_matrix(rotate: &Rotate, matrix: &mut [[i16; 3]; 3]) { + // Test: lets just do roll, about z axis: + let cos_z = cos(Rad(rotate.z as u16)).to_bits() as i16; + let sin_z = sin(Rad(rotate.z as u16)).to_bits() as i16; + matrix[0][0] = cos_z; + matrix[1][0] = -sin_z; + matrix[0][1] = sin_z; + matrix[1][1] = cos_z; + matrix[2][2] = ONE; + +} + +/// Sends matrix to the GTE +fn set_rot_matrix(matrix: [[i16; 3]; 3]) { + delay(); + let mut r1 = gte::RT11_12::new(); + let v1 = (ONE | matrix[0][0]) as u32 | ((matrix[0][1] as u32 ) << 28); + delay(); + r1.assign(v1).store(); + delay(); + let mut r2 = gte::RT13_21::new(); + let v2 = (matrix[0][2]) as u32 | ((matrix[1][0] as u32 ) << 28); + delay(); + r2.assign(v2).store(); + delay(); + let mut r3 = gte::RT22_23::new(); + let v3 = (ONE | matrix[1][1]) as u32 | ((matrix[1][2] as u32 ) << 28); + delay(); + r3.assign(v3).store(); + delay(); + let mut r4 = gte::RT31_32::new(); + let v4 = (matrix[2][0]) as u32 | ((matrix[2][1] as u32 ) << 28); + delay(); + r4.assign(v4).store(); + delay(); + let mut r5 = gte::RT33::new(); + let v5 = ONE | matrix[2][2]; + delay(); + r5.assign(v5).store(); + delay(); + delay(); + delay(); // 3 +} + + +fn rot_trans_pers(v0: (i16, i16, i16), sxy: &mut Vertex, p: &u32, flag: &mut u32) -> i32 { + let flag_reg = gte::FLAG::new(); + let z_reg = gte::IR0::new(); + //let z_reg = gte::SZ0::new(); + let mut vxy0 = gte::VXY0::new(); + let mut vz0 = gte::VZ0::new(); + + // Send vector v0 to GTE: + vxy0.assign((v0.1 as u32) << 16 | (v0.0) as u32).store(); + //delay(); + vz0.assign(v0.2).store(); + //delay(); + + // Send RTPS GTE command to cop2 + unsafe { + core::arch::asm! { + ".long {} & 0x1ffffff | 37 << 25 # cop2", + const RTPS, + options(nomem, nostack) + } + } + + *flag = flag_reg.to_bits(); + //let sx = gte::MAC1::new().to_bits() as i16; + let sx = gte::IR1::new().to_bits() as i16; + delay(); + //let sy = gte::MAC2::new().to_bits() as i16; + let sy = gte::IR2::new().to_bits() as i16; + delay(); + + //assert!(sxa == sx); + //assert!(sya == sy); // These asserts added just enough delay to allow this code to work ... without them, sxy is not correctly set! + // TODO: determine WHERE the correct delay needs to be added! + *sxy = Vertex(sx, sy); + z_reg.to_bits() as i32 // screen Z +} + + +#[no_mangle] +fn main() { + let mut gpu_dma = dma::GPU::new(); + + // Full Hi-res is 640 x 480 as used in primdraw.c, + // can't get a double buffer with that y res + //Framebuffer::new((0, 0), (0, 240), (320, 240), VideoMode::NTSC, Some(INDIGO)).unwrap() + let mut fb = Framebuffer::new((0, 0), (0, 240), (640, 240), VideoMode::NTSC, Some(BLUE)).unwrap(); + + let mut txt = fb.load_default_font().new_text_box((0, 0), (520, 200)); + let mut db = 0; // display buffer 0 or 1 + let mut autorotate = 0; + let mut tpressed = 0; + let mut p = 0; // idk what this is for, used by RTPS? + let mut flag = 0; // GTE status flag + + // Enable GTE: + let mut status = cop0::Status::new(); + status.assign(ENABLE_GTE).store(); + + // If gamepad init occurs before GTE enable, gamepad doesn't work ... + let mut gamepad = Gamepad::new(); + + // Set up 2 x ordering tables in a OT_LENGTH array + let mut polys_a = [const { Packet::new(PolyF3::new()) }; OT_LENGTH]; + let mut polys_b = [const { Packet::new(PolyF3::new()) }; OT_LENGTH]; + let mut my_prims: [PolyF3; 1024] = [Default::default(); 1024]; + link_list(&mut polys_a); + link_list(&mut polys_b); + + // Location and Dimensions of the object + let mut trans = Trans{ x: CENTERX, y: CENTERY, z: 0 }; + let mut rotate = Rotate{ x: 0, y: 0, z: 0 }; + + let (h, w) = (64, 64); + let vec_mesh = [ // list of triangle vertices in 3d space + (50, 50, 10), (25, 25, 10), (50, 10, 10), + //(10, 10, -1), (15, 5, -1), (20, 10, -1), + //(250, 150, 0), (225, 125, 0), (225, 110, 0), + //(50, 50, 0), (25, 25, 0), (50, 10, 0), + + ]; + // OFX, OFY, and H don't appear to be having an effect on screen position, but does on the + // reported coords? + gte::OFX::new().assign((CENTERX as u32) << 16 ).store(); // TODO: should OFX and OFY be i16? NO!, 1 sign, 15bit int, 16 frac + gte::OFY::new().assign((CENTERY as u32) << 16 ).store(); + // SetGeomScreen(CENTERX); + gte::H::new().assign(CENTERX).store(); + + + let mut matrix: [[i16; 3]; 3] = [ + [ONE, 0, 0], + [0, ONE, 0], + [0, 0, ONE], + ]; + + set_rot_matrix(matrix); + + // Main loop + loop { + dprintln!(txt, "after a"); + dprintln!(txt, " SIMPLE 3D EXAMPLE BY LAMEGUY64"); + dprintln!(txt, " 2014 MEIDO-TEK PRODUCTIONS"); + dprintln!(txt, " WWW.PSXDEV.NET"); + dprintln!(txt, "autorotate: {}", autorotate); + //dprintln!(txt, "DEBUG: {:b}", r5.to_bits()); + + let gp = gamepad.poll_p1(); + if autorotate == 0 { + if gp.pressed(Button::L1) { + trans.z -= 4; + } else if gp.pressed(Button::L2) { + trans.z += 4; + } + if gp.pressed(Button::R1) { + rotate.z -= 8; + } else if gp.pressed(Button::R2) { + rotate.z += 8; + } + + if gp.pressed(Button::Triangle) { + trans.y -= 2; + } else if gp.pressed(Button::Cross) { + trans.y += 2; + } + if gp.pressed(Button::Square) { + trans.x -= 4; + } else if gp.pressed(Button::Circle) { + trans.x += 4; + } + + if gp.pressed(Button::Up) { + rotate.y -= 8; + } else if gp.pressed(Button::Down) { + rotate.y += 8; + } + if gp.pressed(Button::Left) { + rotate.x -= 8; + } else if gp.pressed(Button::Right) { + rotate.x += 8; + } + + if gp.pressed(Button::Select) { + rotate = Rotate{x: 0, y: 0, z: 0}; + trans = Trans{x: CENTERX, y: CENTERY, z: 0}; + } + } else { // Autorotate + //rotate.y += 8; // Pan + //rotate.x += 8; // Tilt + rotate.z += 8; // Roll + } + + if gp.pressed(Button::Start) { + if tpressed == 0 { + autorotate = (autorotate + 1) & 1; + rotate = Rotate{x: 0, y: 0, z: 0}; + trans = Trans{x: CENTERX, y: CENTERY, z: 0}; + } + tpressed = 1; + } else { + tpressed = 0; + } + + // TODO: Use trig fns to covnert pitch / yaw / roll / tilt &c to rotation matrix.... + set_rot_matrix(matrix); + + /// Set Translation vector: + delay(); + gte::TRX::new().assign(trans.x as i32).store(); + delay(); + gte::TRY::new().assign(trans.y as i32).store(); + delay(); + gte::TRZ::new().assign(trans.z as i32).store(); + delay(); + delay(); // appears critical + + rot_matrix(&rotate, &mut matrix); + + db = 1 - db; + + let (display, draw) = if db == 1 { (&mut polys_a, &mut polys_b) } else { (&mut polys_b, &mut polys_a) }; + let mut debug_screen_vertex: [Vertex; 3] = [Vertex(0, 0); 3]; + + gpu_dma.send_list_and(display, || { + let mut my_prim = 0; + for i in (0..vec_mesh.len()).step_by(3) { + let mut vertices: [Vertex; 3] = my_prims[my_prim].get_vertices(); + let mut ot_z = rot_trans_pers(vec_mesh[i], &mut vertices[0], &p, &mut flag); + ot_z += rot_trans_pers(vec_mesh[i+1], &mut vertices[1], &p, &mut flag); + ot_z += rot_trans_pers(vec_mesh[i+2], &mut vertices[2], &p, &mut flag); + ot_z /= 3; + //dprintln!(txt, "debug: {:?}", flag); + my_prims[my_prim].set_vertices(vertices); // Do I need my_prims here? just an empty Poly3F? + debug_screen_vertex = vertices; + + draw[my_prim] + .contents.set_vertices(vertices) + .set_color(YELLOW); + my_prim += 1; + } + }); + dprintln!(txt, "last prim: {:?}", debug_screen_vertex); + dprintln!(txt, "FLAG: {:b}", flag); + txt.reset(); + +/* + // Render the sample vector model + t=0; + for (i=0; i 0) && (OTz < OT_LENGTH)) + AddPrim(&myOT[ActivePage][OTz-2], &myPrims[ActivePage][myPrimNum]); + + myPrimNum++; + t+=3; + } +*/ + // 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); + } +}