psp/sys/
gu.rs

1use crate::sys::{
2    self,
3    display::DisplayPixelFormat,
4    ge::{GeBreakParam, GeCommand, GeContext, GeListArgs, GeListState},
5    kernel::SceUid,
6    types::{ScePspFMatrix4, ScePspFVector3, ScePspIMatrix4, ScePspIVector4},
7};
8use core::{ffi::c_void, mem, ptr::addr_of_mut, ptr::null_mut};
9use num_enum::TryFromPrimitive;
10
11#[allow(clippy::approx_constant)]
12pub const GU_PI: f32 = 3.141593;
13
14/// Primitive types
15#[repr(u32)]
16#[derive(Copy, Clone, Debug)]
17pub enum GuPrimitive {
18    /// Single pixel points (1 vertex per primitive)
19    Points = 0,
20    /// Single pixel lines (2 vertices per primitive)
21    Lines = 1,
22    /// Single pixel line-strip (2 vertices for the first primitive, 1 for every following)
23    LineStrip = 2,
24    /// Filled triangles (3 vertices per primitive)
25    Triangles = 3,
26    /// Filled triangles-strip (3 vertices for the first primitive, 1 for every following)
27    TriangleStrip = 4,
28    /// Filled triangle-fan (3 vertices for the first primitive, 1 for every following)
29    TriangleFan = 5,
30    /// Filled blocks (2 vertices per primitive)
31    Sprites = 6,
32}
33
34/// Patch primitive types
35#[repr(u32)]
36#[derive(Copy, Clone, Debug)]
37pub enum PatchPrimitive {
38    /// Single pixel points (1 vertex per primitive)
39    Points = 0,
40    /// Single pixel line-strip (2 vertices for the first primitive, 1 for every following)
41    LineStrip = 2,
42    /// Filled triangles-strip (3 vertices for the first primitive, 1 for every following)
43    TriangleStrip = 4,
44}
45
46/// States
47#[derive(Debug, Clone, Copy, Eq, PartialEq, TryFromPrimitive)]
48#[repr(u32)]
49pub enum GuState {
50    AlphaTest = 0,
51    DepthTest = 1,
52    ScissorTest = 2,
53    StencilTest = 3,
54    Blend = 4,
55    CullFace = 5,
56    Dither = 6,
57    Fog = 7,
58    ClipPlanes = 8,
59    Texture2D = 9,
60    Lighting = 10,
61    Light0 = 11,
62    Light1 = 12,
63    Light2 = 13,
64    Light3 = 14,
65    LineSmooth = 15,
66    PatchCullFace = 16,
67    ColorTest = 17,
68    ColorLogicOp = 18,
69    FaceNormalReverse = 19,
70    PatchFace = 20,
71    Fragment2X = 21,
72}
73
74/// Matrix modes
75#[repr(u32)]
76#[derive(Copy, Clone, Debug)]
77pub enum MatrixMode {
78    Projection = 0,
79    View = 1,
80    Model = 2,
81    Texture = 3,
82}
83
84bitflags::bitflags! {
85    /// The vertex type decides how the vertices align and what kind of
86    /// information they contain.
87    #[repr(transparent)]
88    pub struct VertexType: i32 {
89        /// 8-bit texture coordinates
90        const TEXTURE_8BIT = 1;
91        /// 16-bit texture coordinates
92        const TEXTURE_16BIT = 2;
93        /// 32-bit texture coordinates (float)
94        const TEXTURE_32BITF = 3;
95
96        /// 16-bit color (R5G6B5A0)
97        const COLOR_5650 = 4 << 2;
98        /// 16-bit color (R5G5B5A1)
99        const COLOR_5551 = 5 << 2;
100        /// 16-bit color (R4G4B4A4)
101        const COLOR_4444 = 6 << 2;
102        /// 32-bit color (R8G8B8A8)
103        const COLOR_8888 = 7 << 2;
104
105        /// 8-bit normals
106        const NORMAL_8BIT = 1 << 5;
107        /// 16-bit normals
108        const NORMAL_16BIT = 2 << 5;
109        /// 32-bit normals (float)
110        const NORMAL_32BITF = 3 << 5;
111
112        /// 8-bit vertex position
113        const VERTEX_8BIT = 1 << 7;
114        /// 16-bit vertex position
115        const VERTEX_16BIT = 2 << 7;
116        /// 32-bit vertex position (float)
117        const VERTEX_32BITF = 3 << 7;
118
119        /// 8-bit weights
120        const WEIGHT_8BIT = 1 << 9;
121        /// 16-bit weights
122        const WEIGHT_16BIT = 2 << 9;
123        /// 32-bit weights (float)
124        const WEIGHT_32BITF = 3 << 9;
125
126        /// 8-bit vertex index
127        const INDEX_8BIT = 1 << 11;
128        /// 16-bit vertex index
129        const INDEX_16BIT = 2 << 11;
130
131        // FIXME: Need to document this.
132        // Number of weights (1-8)
133        const WEIGHTS1 = Self::num_weights(1);
134        const WEIGHTS2 = Self::num_weights(2);
135        const WEIGHTS3 = Self::num_weights(3);
136        const WEIGHTS4 = Self::num_weights(4);
137        const WEIGHTS5 = Self::num_weights(5);
138        const WEIGHTS6 = Self::num_weights(6);
139        const WEIGHTS7 = Self::num_weights(7);
140        const WEIGHTS8 = Self::num_weights(8);
141
142        // Number of vertices (1-8)
143        const VERTICES1 = Self::num_vertices(1);
144        const VERTICES2 = Self::num_vertices(2);
145        const VERTICES3 = Self::num_vertices(3);
146        const VERTICES4 = Self::num_vertices(4);
147        const VERTICES5 = Self::num_vertices(5);
148        const VERTICES6 = Self::num_vertices(6);
149        const VERTICES7 = Self::num_vertices(7);
150        const VERTICES8 = Self::num_vertices(8);
151
152        /// Coordinate is passed directly to the rasterizer
153        const TRANSFORM_2D = 1 << 23;
154        /// Coordinate is transformed before being passed to rasterizer
155        const TRANSFORM_3D = 0;
156    }
157}
158
159impl VertexType {
160    const fn num_weights(n: u32) -> i32 {
161        (((n - 1) & 7) << 14) as i32
162    }
163
164    const fn num_vertices(n: u32) -> i32 {
165        (((n - 1) & 7) << 18) as i32
166    }
167}
168
169/// Texture pixel formats
170// TODO: Better documentation
171#[derive(Debug, Clone, Copy)]
172#[repr(u32)]
173pub enum TexturePixelFormat {
174    /// Hicolor, 16-bit.
175    Psm5650 = 0,
176    /// Hicolor, 16-bit
177    Psm5551 = 1,
178    /// Hicolor, 16-bit
179    Psm4444 = 2,
180    /// Truecolor, 32-bit
181    Psm8888 = 3,
182    /// Indexed, 4-bit (2 pixels per byte)
183    PsmT4 = 4,
184    /// Indexed, 8-bit
185    PsmT8 = 5,
186    /// Indexed, 16-bit
187    PsmT16 = 6,
188    /// Indexed, 32-bit
189    PsmT32 = 7,
190    PsmDxt1 = 8,
191    PsmDxt3 = 9,
192    PsmDxt5 = 10,
193}
194
195/// Spline Mode
196#[repr(u32)]
197pub enum SplineMode {
198    FillFill = 0,
199    OpenFill = 1,
200    FillOpen = 2,
201    OpenOpen = 3,
202}
203
204/// Shading Model
205#[repr(u32)]
206// TODO: Should this be `ShadeMode` (no L)?
207pub enum ShadingModel {
208    Flat = 0,
209    Smooth = 1,
210}
211
212/// Logical operation
213#[repr(u32)]
214pub enum LogicalOperation {
215    Clear = 0,
216    And = 1,
217    AndReverse = 2,
218    Copy = 3,
219    AndInverted = 4,
220    Noop = 5,
221    Xor = 6,
222    Or = 7,
223    Nor = 8,
224    Equiv = 9,
225    Inverted = 10,
226    OrReverse = 11,
227    CopyInverted = 12,
228    OrInverted = 13,
229    Nand = 14,
230    Set = 15,
231}
232
233/// Texture Filter
234#[repr(u32)]
235pub enum TextureFilter {
236    Nearest = 0,
237    Linear = 1,
238    NearestMipmapNearest = 4,
239    LinearMipmapNearest = 5,
240    NearestMipmapLinear = 6,
241    LinearMipmapLinear = 7,
242}
243
244/// Texture Map Mode
245#[derive(Debug, Clone, Copy)]
246#[repr(u32)]
247pub enum TextureMapMode {
248    TextureCoords = 0,
249    TextureMatrix = 1,
250    EnvironmentMap = 2,
251}
252
253/// Texture Level Mode
254#[repr(u32)]
255pub enum TextureLevelMode {
256    Auto = 0,
257    Const = 1,
258    Slope = 2,
259}
260
261/// Texture Projection Map Mode
262#[derive(Debug, Clone, Copy)]
263#[repr(u32)]
264pub enum TextureProjectionMapMode {
265    Position = 0,
266    Uv = 1,
267    NormalizedNormal = 2,
268    Normal = 3,
269}
270
271/// Wrap Mode
272#[repr(u32)]
273pub enum GuTexWrapMode {
274    /// The texture repeats after crossing the border
275    Repeat = 0,
276
277    /// Texture clamps at the border
278    Clamp = 1,
279}
280
281/// Front Face Direction
282#[repr(u32)]
283pub enum FrontFaceDirection {
284    Clockwise = 0,
285    CounterClockwise = 1,
286}
287
288/// Test function for alpha test
289#[derive(Copy, Clone, Debug)]
290#[repr(u32)]
291pub enum AlphaFunc {
292    Never = 0,
293    Always,
294    Equal,
295    NotEqual,
296    Less,
297    LessOrEqual,
298    Greater,
299    GreaterOrEqual,
300}
301
302/// Test function for stencil test
303#[derive(Copy, Clone, Debug)]
304#[repr(u32)]
305pub enum StencilFunc {
306    Never = 0,
307    Always,
308    Equal,
309    NotEqual,
310    Less,
311    LessOrEqual,
312    Greater,
313    GreaterOrEqual,
314}
315
316/// Test function for color test
317#[derive(Copy, Clone, Debug)]
318#[repr(u32)]
319pub enum ColorFunc {
320    Never = 0,
321    Always,
322    Equal,
323    NotEqual,
324}
325
326/// Test function for depth test
327#[derive(Copy, Clone, Debug)]
328#[repr(u32)]
329pub enum DepthFunc {
330    /// No pixels pass the depth-test
331    Never = 0,
332    /// All pixels pass the depth-test
333    Always,
334    /// Pixels that match the depth-test pass
335    Equal,
336    /// Pixels that doesn't match the depth-test pass
337    NotEqual,
338    /// Pixels that are less in depth passes
339    Less,
340    /// Pixels that are less or equal in depth passes
341    LessOrEqual,
342    /// Pixels that are greater in depth passes
343    Greater,
344    /// Pixels that are greater or equal passes
345    GreaterOrEqual,
346}
347
348bitflags::bitflags! {
349    /// Clear Buffer Mask
350    #[repr(transparent)]
351    pub struct ClearBuffer: u32 {
352        /// Clears the color buffer.
353        const COLOR_BUFFER_BIT = 1;
354        /// Clears the stencil buffer.
355        const STENCIL_BUFFER_BIT = 2;
356        /// Clears the depth buffer.
357        const DEPTH_BUFFER_BIT = 4;
358        /// Enables fast clearing. This divides the screen into 16 parts
359        /// and clears them in parallel.
360        const FAST_CLEAR_BIT = 16;
361    }
362}
363
364/// Texture effect apply-modes.
365///
366/// Key for the apply-modes:
367/// - `Cv`: Color value result
368/// - `Ct`: Texture color
369/// - `Cf`: Fragment color
370/// - `Cc`: Constant color (specified by `sceGuTexEnvColor`)
371///
372/// The fields TCC_RGB and TCC_RGBA specify components that differ between
373/// the two different component modes.
374#[derive(Debug, Clone, Copy)]
375#[repr(u32)]
376pub enum TextureEffect {
377    // TODO: Better documentation
378    /// The texture is multiplied with the current diffuse fragment.
379    ///
380    /// `Cv=Ct*Cf TCC_RGB: Av=Af TCC_RGBA: Av=At*Af`
381    Modulate = 0,
382    /// `TCC_RGB: Cv=Ct,Av=Af TCC_RGBA: Cv=Cf*(1-At)+Ct*At Av=Af`
383    Decal = 1,
384    /// `Cv=(Cf*(1-Ct))+(Cc*Ct) TCC_RGB: Av=Af TCC_RGBA: Av=At*Af`
385    Blend = 2,
386    /// The texture replaces the fragment
387    ///
388    /// `Cv=Ct TCC_RGB: Av=Af TCC_RGBA: Av=At`
389    Replace = 3,
390    /// The texture is added on-top of the diffuse fragment
391    ///
392    /// `Cv=Cf+Ct TCC_RGB: Av=Af TCC_RGBA: Av=At*Af`
393    Add = 4,
394}
395
396/// Texture color component-modes.
397#[derive(Debug, Clone, Copy)]
398#[repr(u32)]
399pub enum TextureColorComponent {
400    /// The texture alpha does not have any effect.
401    Rgb = 0,
402    /// The texture alpha is taken into account.
403    Rgba = 1,
404}
405
406/// Mipmap Level
407#[derive(Debug, Clone, Copy)]
408#[repr(u32)]
409pub enum MipmapLevel {
410    None = 0,
411    Level1,
412    Level2,
413    Level3,
414    Level4,
415    Level5,
416    Level6,
417    Level7,
418}
419
420/// Blending Operation
421///
422/// Keys for the blending operations:
423///
424/// - `Cs`: Source color
425/// - `Cd`: Destination color
426/// - `Bs`: Blend function for source fragment
427/// - `Bd`: Blend function for destination fragment
428#[repr(u32)]
429pub enum BlendOp {
430    /// `(Cs*Bs) + (Cd*Bd)`
431    Add = 0,
432    /// `(Cs*Bs) - (Cd*Bd)`
433    Subtract = 1,
434    /// `(Cd*Bd) - (Cs*Bs)`
435    ReverseSubtract = 2,
436    /// `Cs < Cd ? Cs : Cd`
437    Min = 3,
438    /// `Cs < Cd ? Cd : Cs`
439    Max = 4,
440    /// `|Cs-Cd|`
441    Abs = 5,
442}
443
444/// Blending factor
445#[repr(u32)]
446pub enum BlendFactor {
447    Color = 0,
448    OneMinusColor = 1,
449    SrcAlpha = 2,
450    OneMinusSrcAlpha = 3,
451    DstAlpha = 4,
452    OneMinusDstAlpha = 5,
453    // TODO: There are likely 4 missing values here.
454    // What are 6, 7, 8, 9? This can probably be determined with some experimentation.
455    // They may also be reserved values.
456    /// Use the fixed values provided in `sceGuBlendFunc`.
457    Fix = 10,
458}
459
460/// Stencil Operations
461#[repr(u32)]
462pub enum StencilOperation {
463    /// Keeps the current value
464    Keep = 0,
465    /// Sets the stencil buffer value to zero
466    Zero = 1,
467    /// Sets the stencil buffer value to ref, as specified by `sceGuStencilFunc`
468    Replace = 2,
469    /// Increments the current stencil buffer value
470    Invert = 3,
471    /// Decrease the current stencil buffer value
472    Incr = 4,
473    /// Bitwise invert the current stencil buffer value
474    Decr = 5,
475}
476
477bitflags::bitflags!(
478    /// Light Components
479    #[repr(transparent)]
480    pub struct LightComponent: i32 {
481        const AMBIENT = 1;
482        const DIFFUSE = 2;
483        const SPECULAR = 4;
484
485        // TODO: What is this?
486        const UNKNOWN_LIGHT_COMPONENT = 8;
487    }
488);
489
490/// Light modes
491#[repr(u32)]
492pub enum LightMode {
493    SingleColor = 0,
494
495    /// Separate specular colors are used to interpolate the specular component
496    /// independently, so that it can be added to the fragment after the texture
497    /// color.
498    SeparateSpecularColor = 1,
499}
500
501/// Light Type
502#[repr(u32)]
503pub enum LightType {
504    Directional = 0,
505    Pointlight = 1,
506    Spotlight = 2,
507}
508
509/// Contexts
510#[repr(u32)]
511#[derive(Copy, Clone, Debug)]
512pub enum GuContextType {
513    Direct = 0,
514    Call = 1,
515    Send = 2,
516}
517
518/// List Queue Mode
519#[repr(u32)]
520pub enum GuQueueMode {
521    /// Place list last in the queue, so it executes in-order
522    Tail = 0,
523    /// Place list first in queue so that it executes as soon as possible
524    Head = 1,
525}
526
527/// Sync mode
528#[repr(u32)]
529pub enum GuSyncMode {
530    /// Wait until the last sceGuFinish command is reached.
531    Finish = 0,
532    /// Wait until the last (?) signal is executed.
533    Signal = 1,
534    /// Wait until all commands currently in list are executed.
535    Done = 2,
536    /// Wait for the currently executed display list (`GuContextType::Direct`).
537    List = 3,
538    /// Wait for the last send list.
539    Send = 4,
540}
541
542/// Sync Behavior
543#[repr(u32)]
544pub enum GuSyncBehavior {
545    /// Wait for the GE list to be completed.
546    Wait = 0,
547    /// Just peek at the current state.
548    NoWait = 1,
549}
550
551/// GU Callback ID
552#[repr(u32)]
553pub enum GuCallbackId {
554    /// Called when `sceGuSignal` is used.
555    Signal = 1,
556
557    /// Called when display list is finished.
558    Finish = 4,
559}
560
561/// Signal behavior
562#[repr(u32)]
563pub enum SignalBehavior {
564    /// Stops display list execution until callback function finished.
565    Suspend = 1,
566    /// Do not stop display list execution during callback.
567    Continue = 2,
568}
569
570/// Map 8-bit color channels into one 32-bit value.
571#[inline]
572pub const fn abgr(a: u8, b: u8, g: u8, r: u8) -> u32 {
573    (r as u32) | ((g as u32) << 8) | ((b as u32) << 16) | ((a as u32) << 24)
574}
575
576/// Map 8-bit color channels into one 32-bit value.
577#[inline]
578pub const fn argb(a: u8, r: u8, g: u8, b: u8) -> u32 {
579    abgr(a, b, g, r)
580}
581
582/// Map 8-bit color channels into one 32-bit value.
583#[inline]
584pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> u32 {
585    argb(a, r, g, b)
586}
587
588#[inline]
589/// Map floating point channels (0..1) into one 32-bit value
590pub fn color(r: f32, g: f32, b: f32, a: f32) -> u32 {
591    rgba(
592        (r * 255.0) as u8,
593        (g * 255.0) as u8,
594        (b * 255.0) as u8,
595        (a * 255.0) as u8,
596    )
597}
598
599pub type GuCallback = Option<extern "C" fn(id: i32, arg: *mut c_void)>;
600pub type GuSwapBuffersCallback =
601    Option<extern "C" fn(display: *mut *mut c_void, render: *mut *mut c_void)>;
602
603struct Settings {
604    sig: GuCallback,
605    fin: GuCallback,
606    signal_history: [i16; 16],
607    signal_offset: u32,
608    kernel_event_flag: SceUid,
609    ge_callback_id: i32,
610    swap_buffers_callback: GuSwapBuffersCallback,
611    swap_buffers_behaviour: crate::sys::DisplaySetBufSync,
612}
613
614struct GuDisplayList {
615    start: *mut u32,
616    current: *mut u32,
617    parent_context: GuContextType,
618}
619
620struct GuContext {
621    list: GuDisplayList,
622    scissor_enable: i32,
623    scissor_start: [i32; 2],
624    scissor_end: [i32; 2],
625    near_plane: i32,
626    far_plane: i32,
627    depth_offset: i32,
628    fragment_2x: i32,
629    texture_function: i32,
630    texture_proj_map_mode: TextureProjectionMapMode,
631    texture_map_mode: TextureMapMode,
632    sprite_mode: [i32; 4],
633    clear_color: u32,
634    clear_stencil: u32,
635    clear_depth: u32,
636    texture_mode: TexturePixelFormat,
637}
638
639struct GuDrawBuffer {
640    pixel_size: DisplayPixelFormat,
641    frame_width: i32,
642    frame_buffer: *mut c_void,
643    disp_buffer: *mut c_void,
644    depth_buffer: *mut c_void,
645    depth_width: i32,
646    width: i32,
647    height: i32,
648}
649
650struct GuLightSettings {
651    /// Light type
652    type_: GeCommand,
653    /// X position
654    xpos: GeCommand,
655    /// Y position
656    ypos: GeCommand,
657    /// Z position
658    zpos: GeCommand,
659    /// X direction
660    xdir: GeCommand,
661    /// Y direction
662    ydir: GeCommand,
663    /// Z direction
664    zdir: GeCommand,
665
666    /// Ambient color
667    ambient: GeCommand,
668    /// Diffuse color
669    diffuse: GeCommand,
670    /// Specular color
671    specular: GeCommand,
672    /// Constant attenuation
673    constant: GeCommand,
674    /// Linear attenuation
675    linear: GeCommand,
676    /// Quadratic attenuation
677    quadratic: GeCommand,
678    /// Light exponent
679    exponent: GeCommand,
680    /// Light cutoff
681    cutoff: GeCommand,
682}
683
684static mut CURRENT_FRAME: u32 = 0;
685static mut CONTEXTS: [GuContext; 3] = [
686    GuContext {
687        list: GuDisplayList {
688            start: null_mut(),
689            current: null_mut(),
690            parent_context: GuContextType::Direct,
691        },
692        scissor_enable: 0,
693        scissor_start: [0, 0],
694        scissor_end: [0, 0],
695        near_plane: 0,
696        far_plane: 0,
697        depth_offset: 0,
698        fragment_2x: 0,
699        texture_function: 0,
700        texture_proj_map_mode: TextureProjectionMapMode::Position,
701        texture_map_mode: TextureMapMode::TextureCoords,
702        sprite_mode: [0, 0, 0, 0],
703        clear_color: 0,
704        clear_stencil: 0,
705        clear_depth: 0,
706        texture_mode: TexturePixelFormat::Psm5650,
707    },
708    GuContext {
709        list: GuDisplayList {
710            start: null_mut(),
711            current: null_mut(),
712            parent_context: GuContextType::Direct,
713        },
714        scissor_enable: 0,
715        scissor_start: [0, 0],
716        scissor_end: [0, 0],
717        near_plane: 0,
718        far_plane: 0,
719        depth_offset: 0,
720        fragment_2x: 0,
721        texture_function: 0,
722        texture_proj_map_mode: TextureProjectionMapMode::Position,
723        texture_map_mode: TextureMapMode::TextureCoords,
724        sprite_mode: [0, 0, 0, 0],
725        clear_color: 0,
726        clear_stencil: 0,
727        clear_depth: 0,
728        texture_mode: TexturePixelFormat::Psm5650,
729    },
730    GuContext {
731        list: GuDisplayList {
732            start: null_mut(),
733            current: null_mut(),
734            parent_context: GuContextType::Direct,
735        },
736        scissor_enable: 0,
737        scissor_start: [0, 0],
738        scissor_end: [0, 0],
739        near_plane: 0,
740        far_plane: 0,
741        depth_offset: 0,
742        fragment_2x: 0,
743        texture_function: 0,
744        texture_proj_map_mode: TextureProjectionMapMode::Position,
745        texture_map_mode: TextureMapMode::TextureCoords,
746        sprite_mode: [0, 0, 0, 0],
747        clear_color: 0,
748        clear_stencil: 0,
749        clear_depth: 0,
750        texture_mode: TexturePixelFormat::Psm5650,
751    },
752];
753
754static mut GE_LIST_EXECUTED: [i32; 2] = [0, 0];
755static mut GE_EDRAM_ADDRESS: *mut c_void = null_mut();
756
757static mut SETTINGS: Settings = Settings {
758    sig: None,
759    fin: None,
760    signal_history: [0; 16],
761    signal_offset: 0,
762
763    // Invalid UID until initialized.
764    kernel_event_flag: SceUid(-1),
765
766    ge_callback_id: 0,
767    swap_buffers_behaviour: crate::sys::DisplaySetBufSync::Immediate,
768    swap_buffers_callback: None,
769};
770
771static mut LIST: *mut GuDisplayList = null_mut();
772static mut CURR_CONTEXT: GuContextType = GuContextType::Direct;
773static mut INIT: i32 = 0;
774static mut DISPLAY_ON: bool = false;
775static mut CALL_MODE: i32 = 0;
776static mut STATES: u32 = 0;
777
778static mut DRAW_BUFFER: GuDrawBuffer = GuDrawBuffer {
779    depth_buffer: null_mut(),
780    frame_buffer: null_mut(),
781    disp_buffer: null_mut(),
782    width: 0,
783    height: 0,
784    depth_width: 0,
785    frame_width: 0,
786    pixel_size: DisplayPixelFormat::Psm5650,
787};
788
789static mut OBJECT_STACK: *mut *mut u32 = null_mut();
790static mut OBJECT_STACK_DEPTH: i32 = 0;
791
792const LIGHT_COMMANDS: [GuLightSettings; 4] = [
793    GuLightSettings {
794        type_: GeCommand::LightType0,
795        xpos: GeCommand::Light0X,
796        ypos: GeCommand::Light0Y,
797        zpos: GeCommand::Light0Z,
798        xdir: GeCommand::Light0DirectionX,
799        ydir: GeCommand::Light0DirectionY,
800        zdir: GeCommand::Light0DirectionZ,
801        ambient: GeCommand::Light0Ambient,
802        diffuse: GeCommand::Light0Diffuse,
803        specular: GeCommand::Light0Specular,
804        constant: GeCommand::Light0ConstantAtten,
805        linear: GeCommand::Light0LinearAtten,
806        quadratic: GeCommand::Light0QuadtraticAtten,
807        exponent: GeCommand::Light0ExponentAtten,
808        cutoff: GeCommand::Light0CutoffAtten,
809    },
810    GuLightSettings {
811        type_: GeCommand::LightType1,
812        xpos: GeCommand::Light1X,
813        ypos: GeCommand::Light1Y,
814        zpos: GeCommand::Light1Z,
815        xdir: GeCommand::Light1DirectionX,
816        ydir: GeCommand::Light1DirectionY,
817        zdir: GeCommand::Light1DirectionZ,
818        ambient: GeCommand::Light1Ambient,
819        diffuse: GeCommand::Light1Diffuse,
820        specular: GeCommand::Light1Specular,
821        constant: GeCommand::Light1ConstantAtten,
822        linear: GeCommand::Light1LinearAtten,
823        quadratic: GeCommand::Light1QuadtraticAtten,
824        exponent: GeCommand::Light1ExponentAtten,
825        cutoff: GeCommand::Light1CutoffAtten,
826    },
827    GuLightSettings {
828        type_: GeCommand::LightType2,
829        xpos: GeCommand::Light2X,
830        ypos: GeCommand::Light2Y,
831        zpos: GeCommand::Light2Z,
832        xdir: GeCommand::Light2DirectionX,
833        ydir: GeCommand::Light2DirectionY,
834        zdir: GeCommand::Light2DirectionZ,
835        ambient: GeCommand::Light2Ambient,
836        diffuse: GeCommand::Light2Diffuse,
837        specular: GeCommand::Light2Specular,
838        constant: GeCommand::Light2ConstantAtten,
839        linear: GeCommand::Light2LinearAtten,
840        quadratic: GeCommand::Light2QuadtraticAtten,
841        exponent: GeCommand::Light2ExponentAtten,
842        cutoff: GeCommand::Light2CutoffAtten,
843    },
844    GuLightSettings {
845        type_: GeCommand::LightType3,
846        xpos: GeCommand::Light3X,
847        ypos: GeCommand::Light3Y,
848        zpos: GeCommand::Light3Z,
849        xdir: GeCommand::Light3DirectionX,
850        ydir: GeCommand::Light3DirectionY,
851        zdir: GeCommand::Light3DirectionZ,
852        ambient: GeCommand::Light3Ambient,
853        diffuse: GeCommand::Light3Diffuse,
854        specular: GeCommand::Light3Specular,
855        constant: GeCommand::Light3ConstantAtten,
856        linear: GeCommand::Light3LinearAtten,
857        quadratic: GeCommand::Light3QuadtraticAtten,
858        exponent: GeCommand::Light3ExponentAtten,
859        cutoff: GeCommand::Light3CutoffAtten,
860    },
861];
862
863#[inline]
864unsafe fn send_command_i(cmd: GeCommand, argument: i32) {
865    (*(*LIST).current) = ((cmd as u32) << 24) | (argument as u32 & 0xffffff);
866    (*LIST).current = (*LIST).current.add(1);
867}
868
869#[inline]
870unsafe fn send_command_f(cmd: GeCommand, argument: f32) {
871    send_command_i(cmd, (argument.to_bits() >> 8) as i32);
872}
873
874#[inline]
875unsafe fn send_command_i_stall(cmd: GeCommand, argument: i32) {
876    send_command_i(cmd, argument);
877    if let (GuContextType::Direct, 0) = (CURR_CONTEXT, OBJECT_STACK_DEPTH) {
878        crate::sys::sceGeListUpdateStallAddr(GE_LIST_EXECUTED[0], (*LIST).current as *mut c_void);
879    }
880}
881
882unsafe fn draw_region(x: i32, y: i32, width: i32, height: i32) {
883    send_command_i(GeCommand::Region1, (y << 10) | x);
884    send_command_i(
885        GeCommand::Region2,
886        (((y + height) - 1) << 10) | ((x + width) - 1),
887    );
888}
889
890unsafe fn reset_values() {
891    INIT = 0;
892    STATES = 0;
893    CURRENT_FRAME = 0;
894    OBJECT_STACK_DEPTH = 0;
895    DISPLAY_ON = false;
896    CALL_MODE = 0;
897    DRAW_BUFFER.pixel_size = DisplayPixelFormat::Psm5551;
898    DRAW_BUFFER.frame_width = 0;
899    DRAW_BUFFER.frame_buffer = null_mut();
900    DRAW_BUFFER.disp_buffer = null_mut();
901    DRAW_BUFFER.depth_buffer = null_mut();
902    DRAW_BUFFER.depth_width = 0;
903    DRAW_BUFFER.width = 480;
904    DRAW_BUFFER.height = 272;
905
906    for i in 0..CONTEXTS.len() {
907        let context = addr_of_mut!(CONTEXTS[i]);
908        (*context).scissor_enable = 0;
909        (*context).scissor_start = [0, 0];
910        (*context).scissor_end = [0, 0];
911
912        (*context).near_plane = 0;
913        (*context).far_plane = 1;
914
915        (*context).depth_offset = 0;
916        (*context).fragment_2x = 0;
917        (*context).texture_function = 0;
918        (*context).texture_proj_map_mode = TextureProjectionMapMode::Position;
919        (*context).texture_map_mode = TextureMapMode::TextureCoords;
920        (*context).sprite_mode[0] = 0;
921        (*context).sprite_mode[1] = 0;
922        (*context).sprite_mode[2] = 0;
923        (*context).sprite_mode[3] = 0;
924        (*context).clear_color = 0;
925        (*context).clear_stencil = 0;
926        (*context).clear_depth = 0xffff;
927        (*context).texture_mode = TexturePixelFormat::Psm5650;
928    }
929
930    SETTINGS.sig = None;
931    SETTINGS.fin = None;
932}
933
934extern "C" fn callback_sig(id: i32, arg: *mut c_void) {
935    let settings = arg as *mut Settings;
936
937    unsafe {
938        let idx = ((*settings).signal_offset & 15) as usize;
939        (*settings).signal_history[idx] = (id & 0xffff) as i16;
940        (*settings).signal_offset += 1;
941
942        if (*settings).sig.is_some() {
943            // Convert Option<fn(i32, *mut c_void)> -> fn(i32)
944            // This is fine because we are transmuting a nullable function
945            // pointer to another function pointer. The requirement here is that
946            // it must not be null.
947            let f = mem::transmute::<_, extern "C" fn(i32)>((*settings).sig);
948
949            f(id & 0xffff);
950        }
951
952        crate::sys::sceKernelSetEventFlag((*settings).kernel_event_flag, 1);
953    }
954}
955
956extern "C" fn callback_fin(id: i32, arg: *mut c_void) {
957    unsafe {
958        let settings = arg as *mut Settings;
959
960        if let Some(fin) = (*settings).fin {
961            // Convert Option<fn(i32, *mut c_void)> -> fn(i32)
962            // This is fine because we are transmuting a nullable function
963            // pointer to another function pointer. The requirement here is that
964            // it must not be null.
965            let f = core::mem::transmute::<_, extern "C" fn(i32)>(fin);
966
967            f(id & 0xffff)
968        }
969    }
970}
971
972/// Set depth buffer parameters
973///
974/// # Parameters
975///
976/// - `zbp`: VRAM pointer where the depth buffer should start
977/// - `zbw`: The width of the depth buffer (block-aligned)
978#[allow(non_snake_case)]
979#[no_mangle]
980pub unsafe extern "C" fn sceGuDepthBuffer(zbp: *mut c_void, zbw: i32) {
981    DRAW_BUFFER.depth_buffer = zbp;
982
983    if DRAW_BUFFER.depth_width == 0 || DRAW_BUFFER.depth_width != zbw {
984        DRAW_BUFFER.depth_width = zbw;
985    }
986
987    send_command_i(GeCommand::ZBufPtr, zbp as i32 & 0xffffff);
988    send_command_i(
989        GeCommand::ZBufWidth,
990        (((zbp as u32 & 0xff000000) >> 8) | zbw as u32) as i32,
991    );
992}
993
994/// Set display buffer parameters
995///
996/// # Parameters
997///
998/// - `width`: Width of the display buffer in pixels
999/// - `height`: Width of the display buffer in pixels
1000/// - `dispbp`: VRAM pointer to where the display-buffer starts
1001/// - `dispbw`: Display buffer width (block aligned)
1002#[allow(non_snake_case)]
1003#[no_mangle]
1004pub unsafe extern "C" fn sceGuDispBuffer(
1005    width: i32,
1006    height: i32,
1007    dispbp: *mut c_void,
1008    dispbw: i32,
1009) {
1010    use crate::sys::DisplaySetBufSync;
1011
1012    DRAW_BUFFER.width = width;
1013    DRAW_BUFFER.height = height;
1014    DRAW_BUFFER.disp_buffer = dispbp;
1015
1016    if DRAW_BUFFER.frame_width == 0 || DRAW_BUFFER.frame_width != dispbw {
1017        DRAW_BUFFER.frame_width = dispbw;
1018    }
1019
1020    draw_region(0, 0, DRAW_BUFFER.width, DRAW_BUFFER.height);
1021
1022    crate::sys::sceDisplaySetMode(
1023        crate::sys::DisplayMode::Lcd,
1024        DRAW_BUFFER.width as usize,
1025        DRAW_BUFFER.height as usize,
1026    );
1027
1028    if DISPLAY_ON {
1029        crate::sys::sceDisplaySetFrameBuf(
1030            (GE_EDRAM_ADDRESS as *mut u8).add(DRAW_BUFFER.disp_buffer as usize),
1031            dispbw as usize,
1032            DRAW_BUFFER.pixel_size,
1033            DisplaySetBufSync::NextFrame,
1034        );
1035    }
1036}
1037
1038/// Set draw buffer parameters (and store in context for buffer-swap)
1039///
1040/// # Parameters
1041///
1042/// - `psm`: Pixel format to use for rendering (and display)
1043/// - `fbp`: VRAM pointer to where the draw buffer starts
1044/// - `fbw`: Frame buffer width (block aligned)
1045#[allow(non_snake_case)]
1046#[no_mangle]
1047pub unsafe extern "C" fn sceGuDrawBuffer(psm: DisplayPixelFormat, fbp: *mut c_void, fbw: i32) {
1048    DRAW_BUFFER.pixel_size = psm;
1049    DRAW_BUFFER.frame_width = fbw;
1050    DRAW_BUFFER.frame_buffer = fbp;
1051
1052    if DRAW_BUFFER.depth_buffer.is_null() && DRAW_BUFFER.height != 0 {
1053        DRAW_BUFFER.depth_buffer =
1054            (fbp as u32 + (((DRAW_BUFFER.height * fbw) as u32) << 2u32)) as *mut c_void;
1055    }
1056
1057    if DRAW_BUFFER.depth_width == 0 {
1058        DRAW_BUFFER.depth_width = fbw;
1059    }
1060
1061    send_command_i(GeCommand::FramebufPixFormat, psm as i32);
1062    send_command_i(
1063        GeCommand::FrameBufPtr,
1064        DRAW_BUFFER.frame_buffer as i32 & 0xffffff,
1065    );
1066    send_command_i(
1067        GeCommand::FrameBufWidth,
1068        ((DRAW_BUFFER.frame_buffer as u32 & 0xff000000) >> 8) as i32 | DRAW_BUFFER.frame_width,
1069    );
1070    send_command_i(
1071        GeCommand::ZBufPtr,
1072        DRAW_BUFFER.depth_buffer as i32 & 0xffffff,
1073    );
1074    send_command_i(
1075        GeCommand::ZBufWidth,
1076        ((DRAW_BUFFER.depth_buffer as u32 & 0xff000000) >> 8) as i32 | DRAW_BUFFER.depth_width,
1077    );
1078}
1079
1080/// Set draw buffer directly, not storing parameters in the context
1081///
1082/// # Parameters
1083///
1084/// - `psm`: Pixel format to use for rendering
1085/// - `fbp`: VRAM pointer to where the draw buffer starts
1086/// - `fbw`: Frame buffer width (block aligned)
1087#[allow(non_snake_case)]
1088#[no_mangle]
1089pub unsafe extern "C" fn sceGuDrawBufferList(psm: DisplayPixelFormat, fbp: *mut c_void, fbw: i32) {
1090    send_command_i(GeCommand::FramebufPixFormat, psm as i32);
1091    send_command_i(GeCommand::FrameBufPtr, fbp as i32 & 0xffffff);
1092    send_command_i(
1093        GeCommand::FrameBufWidth,
1094        ((fbp as u32 & 0xff000000) >> 8) as i32 | fbw,
1095    );
1096}
1097
1098/// Turn display on or off
1099///
1100/// # Parameters
1101///
1102/// - `state`: Turn display on or off
1103///
1104/// # Return Value
1105///
1106/// State of the display prior to this call
1107#[allow(non_snake_case)]
1108#[no_mangle]
1109pub unsafe extern "C" fn sceGuDisplay(state: bool) -> bool {
1110    use crate::sys::DisplaySetBufSync;
1111
1112    if state {
1113        crate::sys::sceDisplaySetFrameBuf(
1114            (GE_EDRAM_ADDRESS as *mut u8).add(DRAW_BUFFER.disp_buffer as usize),
1115            DRAW_BUFFER.frame_width as usize,
1116            DRAW_BUFFER.pixel_size,
1117            DisplaySetBufSync::NextFrame,
1118        );
1119    } else {
1120        crate::sys::sceDisplaySetFrameBuf(
1121            null_mut(),
1122            0,
1123            DisplayPixelFormat::Psm5650,
1124            DisplaySetBufSync::NextFrame,
1125        );
1126    }
1127
1128    DISPLAY_ON = state;
1129    state
1130}
1131
1132/// Select which depth-test function to use
1133///
1134/// # Parameters
1135///
1136/// - `function`: Depth test function to use
1137#[allow(non_snake_case)]
1138#[no_mangle]
1139pub unsafe extern "C" fn sceGuDepthFunc(function: DepthFunc) {
1140    send_command_i(GeCommand::ZTest, function as i32);
1141}
1142
1143/// Mask depth buffer writes
1144///
1145/// # Parameters
1146///
1147/// - `mask`: `1` to disable Z writes, `0` to enable
1148// TODO: Use bool instead?
1149#[allow(non_snake_case)]
1150#[no_mangle]
1151pub unsafe extern "C" fn sceGuDepthMask(mask: i32) {
1152    send_command_i(GeCommand::ZWriteDisable, mask);
1153}
1154
1155#[allow(non_snake_case)]
1156#[no_mangle]
1157pub unsafe extern "C" fn sceGuDepthOffset(offset: i32) {
1158    let context = &mut CONTEXTS[CURR_CONTEXT as usize];
1159    context.depth_offset = offset;
1160    sceGuDepthRange(context.near_plane, context.far_plane);
1161}
1162
1163/// Set which range to use for depth calculations.
1164///
1165/// # Note
1166///
1167/// The depth buffer is inversed, and takes values from 65535 to 0.
1168///
1169/// # Parameters
1170///
1171/// - `near`: Value to use for the near plane
1172/// - `far`: Value to use for the far plane
1173#[allow(non_snake_case)]
1174#[no_mangle]
1175pub unsafe extern "C" fn sceGuDepthRange(mut near: i32, mut far: i32) {
1176    let context = &mut CONTEXTS[CURR_CONTEXT as usize];
1177    let max = near as u32 + far as u32;
1178    let val = ((max >> 31) + max) as i32;
1179    let z = (val >> 1) as f32;
1180
1181    context.near_plane = near;
1182    context.far_plane = far;
1183
1184    send_command_f(GeCommand::ViewportZScale, z - near as f32);
1185    send_command_f(GeCommand::ViewportZCenter, z + context.depth_offset as f32);
1186
1187    if near > far {
1188        mem::swap(&mut near, &mut far);
1189    }
1190
1191    send_command_i(GeCommand::MinZ, near);
1192    send_command_i(GeCommand::MaxZ, far);
1193}
1194
1195#[allow(non_snake_case)]
1196#[no_mangle]
1197pub unsafe extern "C" fn sceGuFog(near: f32, far: f32, color: u32) {
1198    let mut distance = far - near;
1199
1200    if distance != 0.0 {
1201        distance = 1.0 / distance;
1202    }
1203
1204    send_command_i(GeCommand::FogColor, (color & 0xffffff) as i32);
1205    send_command_f(GeCommand::Fog1, far);
1206    send_command_f(GeCommand::Fog2, distance);
1207}
1208
1209/// Initalize the GU system
1210///
1211/// This function MUST be called as the first function, otherwise state is undetermined.
1212#[allow(non_snake_case)]
1213#[no_mangle]
1214pub unsafe extern "C" fn sceGuInit() {
1215    const INIT_COMMANDS: [GeCommand; 223] = [
1216        GeCommand::Vaddr,
1217        GeCommand::Iaddr,
1218        GeCommand::Base,
1219        GeCommand::VertexType,
1220        GeCommand::OffsetAddr,
1221        GeCommand::Region1,
1222        GeCommand::Region2,
1223        GeCommand::LightingEnable,
1224        GeCommand::LightEnable0,
1225        GeCommand::LightEnable1,
1226        GeCommand::LightEnable2,
1227        GeCommand::LightEnable3,
1228        GeCommand::DepthClampEnable,
1229        GeCommand::CullFaceEnable,
1230        GeCommand::TextureMapEnable,
1231        GeCommand::FogEnable,
1232        GeCommand::DitherEnable,
1233        GeCommand::AlphaBlendEnable,
1234        GeCommand::AlphaTestEnable,
1235        GeCommand::ZTestEnable,
1236        GeCommand::StencilTestEnable,
1237        GeCommand::AntiAliasEnable,
1238        GeCommand::PatchCullEnable,
1239        GeCommand::ColorTestEnable,
1240        GeCommand::LogicOpEnable,
1241        GeCommand::BoneMatrixNumber,
1242        GeCommand::BoneMatrixData,
1243        GeCommand::MorphWeight0,
1244        GeCommand::MorphWeight1,
1245        GeCommand::MorphWeight2,
1246        GeCommand::MorphWeight3,
1247        GeCommand::MorphWeight4,
1248        GeCommand::MorphWeight5,
1249        GeCommand::MorphWeight6,
1250        GeCommand::MorphWeight7,
1251        GeCommand::PatchDivision,
1252        GeCommand::PatchPrimitive,
1253        GeCommand::PatchFacing,
1254        GeCommand::WorldMatrixNumber,
1255        GeCommand::WorldMatrixData,
1256        GeCommand::ViewMatrixNumber,
1257        GeCommand::ViewMatrixData,
1258        GeCommand::ProjMatrixNumber,
1259        GeCommand::ProjMatrixData,
1260        GeCommand::TGenMatrixNumber,
1261        GeCommand::TGenMatrixData,
1262        GeCommand::ViewportXScale,
1263        GeCommand::ViewportYScale,
1264        GeCommand::ViewportZScale,
1265        GeCommand::ViewportXCenter,
1266        GeCommand::ViewportYCenter,
1267        GeCommand::ViewportZCenter,
1268        GeCommand::TexScaleU,
1269        GeCommand::TexScaleV,
1270        GeCommand::TexOffsetU,
1271        GeCommand::TexOffsetV,
1272        GeCommand::OffsetX,
1273        GeCommand::OffsetY,
1274        GeCommand::ShadeMode,
1275        GeCommand::ReverseNormal,
1276        GeCommand::MaterialUpdate,
1277        GeCommand::MaterialEmissive,
1278        GeCommand::MaterialAmbient,
1279        GeCommand::MaterialDiffuse,
1280        GeCommand::MaterialSpecular,
1281        GeCommand::MaterialAlpha,
1282        GeCommand::MaterialSpecularCoef,
1283        GeCommand::AmbientColor,
1284        GeCommand::AmbientAlpha,
1285        GeCommand::LightMode,
1286        GeCommand::LightType0,
1287        GeCommand::LightType1,
1288        GeCommand::LightType2,
1289        GeCommand::LightType3,
1290        GeCommand::Light0X,
1291        GeCommand::Light0Y,
1292        GeCommand::Light0Z,
1293        GeCommand::Light1X,
1294        GeCommand::Light1Y,
1295        GeCommand::Light1Z,
1296        GeCommand::Light2X,
1297        GeCommand::Light2Y,
1298        GeCommand::Light2Z,
1299        GeCommand::Light3X,
1300        GeCommand::Light3Y,
1301        GeCommand::Light3Z,
1302        GeCommand::Light0DirectionX,
1303        GeCommand::Light0DirectionY,
1304        GeCommand::Light0DirectionZ,
1305        GeCommand::Light1DirectionX,
1306        GeCommand::Light1DirectionY,
1307        GeCommand::Light1DirectionZ,
1308        GeCommand::Light2DirectionX,
1309        GeCommand::Light2DirectionY,
1310        GeCommand::Light2DirectionZ,
1311        GeCommand::Light3DirectionX,
1312        GeCommand::Light3DirectionY,
1313        GeCommand::Light3DirectionZ,
1314        GeCommand::Light0ConstantAtten,
1315        GeCommand::Light0LinearAtten,
1316        GeCommand::Light0QuadtraticAtten,
1317        GeCommand::Light1ConstantAtten,
1318        GeCommand::Light1LinearAtten,
1319        GeCommand::Light1QuadtraticAtten,
1320        GeCommand::Light2ConstantAtten,
1321        GeCommand::Light2LinearAtten,
1322        GeCommand::Light2QuadtraticAtten,
1323        GeCommand::Light3ConstantAtten,
1324        GeCommand::Light3LinearAtten,
1325        GeCommand::Light3QuadtraticAtten,
1326        GeCommand::Light0ExponentAtten,
1327        GeCommand::Light1ExponentAtten,
1328        GeCommand::Light2ExponentAtten,
1329        GeCommand::Light3ExponentAtten,
1330        GeCommand::Light0CutoffAtten,
1331        GeCommand::Light1CutoffAtten,
1332        GeCommand::Light2CutoffAtten,
1333        GeCommand::Light3CutoffAtten,
1334        GeCommand::Light0Ambient,
1335        GeCommand::Light0Diffuse,
1336        GeCommand::Light0Specular,
1337        GeCommand::Light1Ambient,
1338        GeCommand::Light1Diffuse,
1339        GeCommand::Light1Specular,
1340        GeCommand::Light2Ambient,
1341        GeCommand::Light2Diffuse,
1342        GeCommand::Light2Specular,
1343        GeCommand::Light3Ambient,
1344        GeCommand::Light3Diffuse,
1345        GeCommand::Light3Specular,
1346        GeCommand::Cull,
1347        GeCommand::FrameBufPtr,
1348        GeCommand::FrameBufWidth,
1349        GeCommand::ZBufPtr,
1350        GeCommand::ZBufWidth,
1351        GeCommand::TexAddr0,
1352        GeCommand::TexAddr1,
1353        GeCommand::TexAddr2,
1354        GeCommand::TexAddr3,
1355        GeCommand::TexAddr4,
1356        GeCommand::TexAddr5,
1357        GeCommand::TexAddr6,
1358        GeCommand::TexAddr7,
1359        GeCommand::TexBufWidth0,
1360        GeCommand::TexBufWidth1,
1361        GeCommand::TexBufWidth2,
1362        GeCommand::TexBufWidth3,
1363        GeCommand::TexBufWidth4,
1364        GeCommand::TexBufWidth5,
1365        GeCommand::TexBufWidth6,
1366        GeCommand::TexBufWidth7,
1367        GeCommand::ClutAddr,
1368        GeCommand::ClutAddrUpper,
1369        GeCommand::TransferSrc,
1370        GeCommand::TransferSrcW,
1371        GeCommand::TransferDst,
1372        GeCommand::TransferDstW,
1373        GeCommand::TexSize0,
1374        GeCommand::TexSize1,
1375        GeCommand::TexSize2,
1376        GeCommand::TexSize3,
1377        GeCommand::TexSize4,
1378        GeCommand::TexSize5,
1379        GeCommand::TexSize6,
1380        GeCommand::TexSize7,
1381        GeCommand::TexMapMode,
1382        GeCommand::TexShadeLs,
1383        GeCommand::TexMode,
1384        GeCommand::TexFormat,
1385        GeCommand::LoadClut,
1386        GeCommand::ClutFormat,
1387        GeCommand::TexFilter,
1388        GeCommand::TexWrap,
1389        GeCommand::TexLevel,
1390        GeCommand::TexFunc,
1391        GeCommand::TexEnvColor,
1392        GeCommand::TexFlush,
1393        GeCommand::TexSync,
1394        GeCommand::Fog1,
1395        GeCommand::Fog2,
1396        GeCommand::FogColor,
1397        GeCommand::TexLodSlope,
1398        GeCommand::FramebufPixFormat,
1399        GeCommand::ClearMode,
1400        GeCommand::Scissor1,
1401        GeCommand::Scissor2,
1402        GeCommand::MinZ,
1403        GeCommand::MaxZ,
1404        GeCommand::ColorTest,
1405        GeCommand::ColorRef,
1406        GeCommand::ColorTestmask,
1407        GeCommand::AlphaTest,
1408        GeCommand::StencilTest,
1409        GeCommand::StencilOp,
1410        GeCommand::ZTest,
1411        GeCommand::BlendMode,
1412        GeCommand::BlendFixedA,
1413        GeCommand::BlendFixedB,
1414        GeCommand::Dith0,
1415        GeCommand::Dith1,
1416        GeCommand::Dith2,
1417        GeCommand::Dith3,
1418        GeCommand::LogicOp,
1419        GeCommand::ZWriteDisable,
1420        GeCommand::MaskRgb,
1421        GeCommand::MaskAlpha,
1422        GeCommand::TransferSrcPos,
1423        GeCommand::TransferDstPos,
1424        GeCommand::TransferSize,
1425        GeCommand::Vscx,
1426        GeCommand::Vscy,
1427        GeCommand::Vscz,
1428        GeCommand::Vtcs,
1429        GeCommand::Vtct,
1430        GeCommand::Vtcq,
1431        GeCommand::Vcv,
1432        GeCommand::Vap,
1433        GeCommand::Vfc,
1434        GeCommand::Vscv,
1435        GeCommand::Finish,
1436        GeCommand::End,
1437        GeCommand::Nop,
1438        GeCommand::Nop,
1439    ];
1440
1441    static INIT_LIST: crate::Align16<[u32; 223]> = crate::Align16({
1442        let mut out = [0; 223];
1443
1444        let mut i = 0;
1445        while i < 223 {
1446            out[i] = (INIT_COMMANDS[i] as u32) << 24;
1447            i += 1;
1448        }
1449
1450        out
1451    });
1452
1453    let mut callback = crate::sys::GeCallbackData {
1454        signal_func: Some(callback_sig),
1455        signal_arg: addr_of_mut!(SETTINGS).cast::<c_void>(),
1456        finish_func: Some(callback_fin),
1457        finish_arg: addr_of_mut!(SETTINGS).cast::<c_void>(),
1458    };
1459
1460    SETTINGS.ge_callback_id = crate::sys::sceGeSetCallback(&mut callback);
1461    SETTINGS.swap_buffers_callback = None;
1462    SETTINGS.swap_buffers_behaviour = super::display::DisplaySetBufSync::Immediate;
1463
1464    GE_EDRAM_ADDRESS = sys::sceGeEdramGetAddr().cast::<c_void>();
1465
1466    GE_LIST_EXECUTED[0] = sys::sceGeListEnQueue(
1467        (&INIT_LIST as *const _ as u32 & 0x1fffffff) as *const _,
1468        core::ptr::null_mut(),
1469        SETTINGS.ge_callback_id,
1470        core::ptr::null_mut(),
1471    );
1472
1473    reset_values();
1474
1475    SETTINGS.kernel_event_flag = super::kernel::sceKernelCreateEventFlag(
1476        b"SceGuSignal\0" as *const u8,
1477        super::kernel::EventFlagAttributes::WAIT_MULTIPLE,
1478        3,
1479        null_mut(),
1480    );
1481
1482    sys::sceGeListSync(GE_LIST_EXECUTED[0], 0);
1483}
1484
1485/// Shutdown the GU system
1486///
1487/// Called when GU is no longer needed
1488#[allow(non_snake_case)]
1489#[no_mangle]
1490pub unsafe extern "C" fn sceGuTerm() {
1491    sys::sceKernelDeleteEventFlag(SETTINGS.kernel_event_flag);
1492    sys::sceGeUnsetCallback(SETTINGS.ge_callback_id);
1493}
1494
1495/// # Parameters
1496///
1497/// - `mode`: If set to 1, reset all the queues.
1498#[allow(non_snake_case)]
1499#[no_mangle]
1500pub unsafe extern "C" fn sceGuBreak(mode: i32) {
1501    static mut UNUSED_BREAK: GeBreakParam = GeBreakParam { buf: [0; 4] };
1502
1503    sys::sceGeBreak(mode, addr_of_mut!(UNUSED_BREAK));
1504}
1505
1506#[allow(non_snake_case)]
1507#[no_mangle]
1508pub unsafe extern "C" fn sceGuContinue() {
1509    // Return this?
1510    sys::sceGeContinue();
1511}
1512
1513// FIXME: This documentation is confusing.
1514/// Setup signal handler
1515///
1516/// # Parameters
1517///
1518/// - `signal`: Signal index to install a handler for
1519/// - `callback`: Callback to call when signal index is triggered
1520///
1521/// # Return Value
1522///
1523/// The old callback handler
1524#[allow(non_snake_case)]
1525#[no_mangle]
1526pub unsafe extern "C" fn sceGuSetCallback(
1527    signal: GuCallbackId,
1528    callback: GuCallback,
1529) -> GuCallback {
1530    let old_callback;
1531
1532    match signal {
1533        GuCallbackId::Signal => {
1534            old_callback = SETTINGS.sig;
1535            SETTINGS.sig = callback;
1536        }
1537
1538        GuCallbackId::Finish => {
1539            old_callback = SETTINGS.fin;
1540            SETTINGS.fin = callback;
1541        }
1542    }
1543
1544    old_callback
1545}
1546
1547// FIXME: This documentation is confusing.
1548/// Trigger signal to call code from the command stream
1549///
1550/// # Parameters
1551///
1552/// - `behavior`: Behavior type
1553/// - `signal`: Signal to trigger
1554#[allow(non_snake_case)]
1555#[no_mangle]
1556pub unsafe extern "C" fn sceGuSignal(behavior: SignalBehavior, signal: i32) {
1557    send_command_i(
1558        GeCommand::Signal,
1559        ((signal & 0xff) << 16) | (behavior as i32 & 0xffff),
1560    );
1561    send_command_i(GeCommand::End, 0);
1562
1563    if signal == 3 {
1564        send_command_i(GeCommand::Finish, 0);
1565        send_command_i(GeCommand::End, 0);
1566    }
1567
1568    send_command_i_stall(GeCommand::Nop, 0);
1569}
1570
1571/// Send raw float command to the GE
1572///
1573/// The argument is converted into a 24-bit float before transfer.
1574///
1575/// # Parameters
1576///
1577/// - `cmd`: Which command to send
1578/// - `argument`: Argument to pass along
1579#[allow(non_snake_case)]
1580#[no_mangle]
1581pub unsafe extern "C" fn sceGuSendCommandf(cmd: GeCommand, argument: f32) {
1582    send_command_f(cmd, argument);
1583}
1584
1585/// Send raw command to the GE
1586///
1587/// Only the 24 lower bits of the argument are passed along.
1588///
1589/// # Parameters
1590///
1591/// - `cmd`: Which command to send
1592/// - `argument`: Argument to pass along
1593#[allow(non_snake_case)]
1594#[no_mangle]
1595pub unsafe extern "C" fn sceGuSendCommandi(cmd: GeCommand, argument: i32) {
1596    send_command_i(cmd, argument);
1597}
1598
1599/// Allocate memory on the current display list for temporary storage
1600///
1601/// # Note
1602///
1603/// This function is NOT for permanent memory allocation, the memory will be
1604/// invalid as soon as you start filling the same display list again.
1605///
1606/// # Parameters
1607///
1608/// - `size`: How much memory to allocate
1609///
1610/// # Return Value
1611///
1612/// Memory-block ready for use
1613#[allow(non_snake_case)]
1614#[no_mangle]
1615pub unsafe extern "C" fn sceGuGetMemory(mut size: i32) -> *mut c_void {
1616    // Some kind of 4-byte alignment?
1617    size += 3;
1618    size += (((size >> 31) as u32) >> 30) as i32;
1619    size = (size >> 2) << 2;
1620
1621    let orig_ptr = (*LIST).current;
1622    let new_ptr = (orig_ptr as usize + size as usize + 8) as *mut u32;
1623
1624    let lo = (8 << 24) | (new_ptr as i32 & 0xffffff);
1625    let hi = ((16 << 24) | ((new_ptr as u32 >> 8) & 0xf0000)) as i32;
1626
1627    *orig_ptr = hi as u32;
1628    *orig_ptr.offset(1) = lo as u32;
1629
1630    (*LIST).current = new_ptr;
1631
1632    if let GuContextType::Direct = CURR_CONTEXT {
1633        crate::sys::sceGeListUpdateStallAddr(GE_LIST_EXECUTED[0], new_ptr.cast::<c_void>());
1634    }
1635
1636    orig_ptr.add(2).cast::<c_void>()
1637}
1638
1639/// Start filling a new display-context
1640///
1641/// The previous context-type is stored so that it can be restored at `sceGuFinish`.
1642///
1643/// # Parameters
1644///
1645/// - `cid`: Context Type
1646/// - `list`: Pointer to display-list (16 byte aligned)
1647#[allow(non_snake_case)]
1648#[no_mangle]
1649pub unsafe extern "C" fn sceGuStart(context_type: GuContextType, list: *mut c_void) {
1650    let context = &mut CONTEXTS[context_type as usize];
1651    let local_list = ((list as u32) | 0x4000_0000) as *mut u32;
1652
1653    // setup display list
1654    context.list.start = local_list;
1655    context.list.current = local_list;
1656    context.list.parent_context = CURR_CONTEXT;
1657    LIST = &mut context.list;
1658
1659    // store current context
1660    CURR_CONTEXT = context_type;
1661
1662    if let GuContextType::Direct = context_type {
1663        GE_LIST_EXECUTED[0] = crate::sys::sceGeListEnQueue(
1664            local_list as *mut c_void,
1665            local_list as *mut c_void,
1666            SETTINGS.ge_callback_id,
1667            core::ptr::null_mut(),
1668        );
1669
1670        SETTINGS.signal_offset = 0;
1671    }
1672
1673    if INIT == 0 {
1674        static DITHER_MATRIX: ScePspIMatrix4 = ScePspIMatrix4 {
1675            x: ScePspIVector4 {
1676                x: -4,
1677                y: 0,
1678                z: -3,
1679                w: 1,
1680            },
1681            y: ScePspIVector4 {
1682                x: 2,
1683                y: -2,
1684                z: 3,
1685                w: -1,
1686            },
1687            z: ScePspIVector4 {
1688                x: -3,
1689                y: 1,
1690                z: -4,
1691                w: 0,
1692            },
1693            w: ScePspIVector4 {
1694                x: 3,
1695                y: -1,
1696                z: 2,
1697                w: -2,
1698            },
1699        };
1700
1701        sceGuSetDither(&DITHER_MATRIX);
1702        sceGuPatchDivide(16, 16);
1703        sceGuColorMaterial(
1704            LightComponent::AMBIENT | LightComponent::DIFFUSE | LightComponent::SPECULAR,
1705        );
1706
1707        sceGuSpecular(1.0);
1708        sceGuTexScale(1.0, 1.0);
1709
1710        INIT = 1;
1711    }
1712
1713    if let GuContextType::Direct = CURR_CONTEXT {
1714        if DRAW_BUFFER.frame_width != 0 {
1715            send_command_i(
1716                GeCommand::FrameBufPtr,
1717                DRAW_BUFFER.frame_buffer as i32 & 0xffffff,
1718            );
1719            send_command_i(
1720                GeCommand::FrameBufWidth,
1721                ((DRAW_BUFFER.frame_buffer as u32 & 0xff00_0000) >> 8) as i32
1722                    | DRAW_BUFFER.frame_width,
1723            );
1724        }
1725    }
1726}
1727
1728/// Finish current display list and go back to the parent context
1729///
1730/// If the context is `Direct`, the stall-address is updated so that the entire
1731/// list will execute. Otherwise, only the terminating action is written to the
1732/// list, depending on the context type.
1733///
1734/// The finish-callback will get a zero as argument when using this function.
1735///
1736/// This also restores control back to whatever context that was active prior to
1737/// this call.
1738///
1739/// # Return Value
1740///
1741/// Size of finished display list
1742#[allow(non_snake_case)]
1743#[no_mangle]
1744pub unsafe extern "C" fn sceGuFinish() -> i32 {
1745    match CURR_CONTEXT {
1746        GuContextType::Direct | GuContextType::Send => {
1747            send_command_i(GeCommand::Finish, 0);
1748            send_command_i_stall(GeCommand::End, 0);
1749        }
1750
1751        GuContextType::Call => {
1752            if CALL_MODE == 1 {
1753                send_command_i(GeCommand::Signal, 0x120000);
1754                send_command_i(GeCommand::End, 0);
1755                send_command_i_stall(GeCommand::Nop, 0);
1756            } else {
1757                send_command_i(GeCommand::Ret, 0);
1758            }
1759        }
1760    }
1761
1762    let size = ((*LIST).current as usize) - ((*LIST).start as usize);
1763
1764    // Go to parent list
1765    CURR_CONTEXT = (*LIST).parent_context;
1766    LIST = &mut CONTEXTS[CURR_CONTEXT as usize].list;
1767    size as i32
1768}
1769
1770/// Finish current display list and go back to the parent context, sending
1771/// argument id for the finish callback.
1772///
1773/// If the context is `Direct`, the stall-address is updated so that the entire
1774/// list will execute. Otherwise, only the terminating action is written to the
1775/// list, depending on the context type.
1776///
1777/// # Parameters
1778///
1779/// - `id`: Finish callback id (16-bit)
1780///
1781/// # Return Value
1782///
1783/// Size of finished display list
1784#[allow(non_snake_case)]
1785#[no_mangle]
1786pub unsafe extern "C" fn sceGuFinishId(id: u32) -> i32 {
1787    match CURR_CONTEXT {
1788        GuContextType::Direct | GuContextType::Send => {
1789            send_command_i(GeCommand::Finish, (id & 0xffff) as i32);
1790            send_command_i_stall(GeCommand::End, 0);
1791        }
1792
1793        GuContextType::Call => {
1794            if CALL_MODE == 1 {
1795                send_command_i(GeCommand::Signal, 0x120000);
1796                send_command_i(GeCommand::End, 0);
1797                send_command_i_stall(GeCommand::Nop, 0);
1798            } else {
1799                send_command_i(GeCommand::Ret, 0);
1800            }
1801        }
1802    }
1803
1804    let size = ((*LIST).current as usize) - ((*LIST).start as usize);
1805
1806    // Go to parent list
1807    CURR_CONTEXT = (*LIST).parent_context;
1808    LIST = &mut CONTEXTS[CURR_CONTEXT as usize].list;
1809    size as i32
1810}
1811
1812/// Call previously generated display-list
1813///
1814/// # Parameters
1815///
1816/// - `list`: Display list to call
1817#[allow(non_snake_case)]
1818#[no_mangle]
1819pub unsafe extern "C" fn sceGuCallList(list: *const c_void) {
1820    let list_addr = list as u32;
1821
1822    if CALL_MODE == 1 {
1823        send_command_i(GeCommand::Signal, (list_addr >> 16) as i32 | 0x110000);
1824        send_command_i(GeCommand::End, list_addr as i32 & 0xffff);
1825        send_command_i_stall(GeCommand::Nop, 0);
1826    } else {
1827        send_command_i(GeCommand::Base, (list_addr >> 8) as i32 & 0xf0000);
1828        send_command_i_stall(GeCommand::Call, list_addr as i32 & 0xffffff);
1829    }
1830}
1831
1832/// Set whether to use stack-based calls or signals to handle execution of
1833/// called lists.
1834///
1835/// # Parameters
1836///
1837/// - `mode`: True (1) to enable signals, false (0) to disable signals and use
1838///   normal calls instead.
1839#[allow(non_snake_case)]
1840#[no_mangle]
1841pub unsafe extern "C" fn sceGuCallMode(mode: i32) {
1842    CALL_MODE = mode;
1843}
1844
1845/// Check how large the current display list is
1846///
1847/// # Return Value
1848///
1849/// The size of the current display list
1850#[allow(non_snake_case)]
1851#[no_mangle]
1852pub unsafe extern "C" fn sceGuCheckList() -> i32 {
1853    (*LIST).current.sub((*LIST).start as usize) as i32
1854}
1855
1856/// Send a list to the GE directly
1857///
1858/// # Parameters
1859///
1860/// - `mode`: Whether to place the list first or last in queue
1861/// - `list`: List to send
1862/// - `context`: Temporary storage for the GE context
1863#[allow(non_snake_case)]
1864#[no_mangle]
1865pub unsafe extern "C" fn sceGuSendList(
1866    mode: GuQueueMode,
1867    list: *const c_void,
1868    context: *mut GeContext,
1869) {
1870    SETTINGS.signal_offset = 0;
1871
1872    let mut args = GeListArgs {
1873        size: 8,
1874        context,
1875        ..<_>::default()
1876    };
1877
1878    let callback = SETTINGS.ge_callback_id;
1879
1880    let list_id = match mode {
1881        GuQueueMode::Head => {
1882            crate::sys::sceGeListEnQueueHead(list, null_mut(), callback, &mut args)
1883        }
1884
1885        GuQueueMode::Tail => crate::sys::sceGeListEnQueue(list, null_mut(), callback, &mut args),
1886    };
1887
1888    GE_LIST_EXECUTED[1] = list_id;
1889}
1890
1891/// Swap display and draw buffer
1892///
1893/// # Return Value
1894///
1895/// Pointer to the new drawbuffer
1896#[allow(non_snake_case)]
1897#[no_mangle]
1898pub unsafe extern "C" fn sceGuSwapBuffers() -> *mut c_void {
1899    if let Some(cb) = SETTINGS.swap_buffers_callback {
1900        cb(
1901            addr_of_mut!(DRAW_BUFFER.disp_buffer),
1902            addr_of_mut!(DRAW_BUFFER.frame_buffer),
1903        );
1904    } else {
1905        mem::swap(&mut DRAW_BUFFER.disp_buffer, &mut DRAW_BUFFER.frame_buffer);
1906    }
1907
1908    if DISPLAY_ON {
1909        crate::sys::sceDisplaySetFrameBuf(
1910            GE_EDRAM_ADDRESS.add(DRAW_BUFFER.disp_buffer as usize) as *const u8,
1911            DRAW_BUFFER.frame_width as usize,
1912            DRAW_BUFFER.pixel_size,
1913            SETTINGS.swap_buffers_behaviour,
1914        );
1915    }
1916
1917    // Comment from the C PSPSDK: remove this? it serves no real purpose
1918    CURRENT_FRAME ^= 1;
1919
1920    DRAW_BUFFER.frame_buffer
1921}
1922
1923/// Wait until display list has finished executing
1924///
1925/// # Parameters
1926///
1927/// - `mode`: What to wait for, one of `GuSyncMode`
1928/// - `behavior`: How to sync, one of `GuSyncBehavior`
1929///
1930/// # Return Value
1931///
1932/// Unknown at this time. GeListState?
1933#[allow(non_snake_case)]
1934#[no_mangle]
1935pub unsafe extern "C" fn sceGuSync(mode: GuSyncMode, behavior: GuSyncBehavior) -> GeListState {
1936    match mode {
1937        GuSyncMode::Finish => crate::sys::sceGeDrawSync(behavior as i32),
1938        GuSyncMode::List => crate::sys::sceGeListSync(GE_LIST_EXECUTED[0], behavior as i32),
1939        GuSyncMode::Send => crate::sys::sceGeListSync(GE_LIST_EXECUTED[1], behavior as i32),
1940        _ => GeListState::Done,
1941    }
1942}
1943
1944// FIXME: Confusing documentation.
1945/// Draw array of vertices forming primitives
1946///
1947/// Vertex order:
1948///
1949/// - Weights (0-8)
1950/// - Texture UV
1951/// - Color
1952/// - Normal
1953/// - Position
1954///
1955/// # Note
1956///
1957/// Every vertex must align to 32 bits, which means that you HAVE to pad if it does not add up!
1958///
1959/// # Parameters
1960///
1961/// - `prim`: What kind of primitives to render
1962/// - `vtype`: Vertex type to process
1963/// - `count`: How many vertices to process
1964/// - `indices`: Optional pointer to an index-list
1965/// - `vertices`: Pointer to a vertex-list
1966#[allow(non_snake_case)]
1967#[no_mangle]
1968pub unsafe extern "C" fn sceGuDrawArray(
1969    prim: GuPrimitive,
1970    vtype: VertexType,
1971    count: i32,
1972    indices: *const c_void,
1973    vertices: *const c_void,
1974) {
1975    if !vtype.is_empty() {
1976        send_command_i(GeCommand::VertexType, vtype.bits());
1977    }
1978
1979    if !indices.is_null() {
1980        send_command_i(GeCommand::Base, (indices as u32 >> 8) as i32 & 0xf0000);
1981        send_command_i(GeCommand::Iaddr, indices as i32 & 0xffffff);
1982    }
1983
1984    if !vertices.is_null() {
1985        send_command_i(GeCommand::Base, (vertices as u32 >> 8) as i32 & 0xf0000);
1986        send_command_i(GeCommand::Vaddr, vertices as i32 & 0xffffff);
1987    }
1988
1989    send_command_i_stall(GeCommand::Prim, ((prim as i32) << 16) | count);
1990}
1991
1992/// Begin conditional rendering of object
1993///
1994/// If no vertices passed into this function are inside the scissor region, it
1995/// will skip rendering the object. There can be up to 32 levels of conditional
1996/// testing, and all levels HAVE to be terminated by sceGuEndObject().
1997///
1998/// # Parameters
1999///
2000/// - `vtype`: Vertex type to process
2001/// - `count`: Number of vertices to test
2002/// - `indices`: Optional list to an index-list
2003/// - `vertices`: Pointer to a vertex-list
2004#[allow(non_snake_case)]
2005#[no_mangle]
2006pub unsafe extern "C" fn sceGuBeginObject(
2007    vtype: i32,
2008    count: i32,
2009    indices: *const c_void,
2010    vertices: *const c_void,
2011) {
2012    if vtype != 0 {
2013        send_command_i(GeCommand::VertexType, vtype);
2014    }
2015
2016    if !indices.is_null() {
2017        send_command_i(GeCommand::Base, (indices as u32 >> 8) as i32 & 0xf0000);
2018        send_command_i(GeCommand::Iaddr, indices as i32 & 0xffffff);
2019    }
2020
2021    if !vertices.is_null() {
2022        send_command_i(GeCommand::Base, (vertices as u32 >> 8) as i32 & 0xf0000);
2023        send_command_i(GeCommand::Vaddr, vertices as i32 & 0xffffff);
2024    }
2025
2026    send_command_i(GeCommand::BoundingBox, count);
2027
2028    // Store start to new object
2029    (*OBJECT_STACK.offset(OBJECT_STACK_DEPTH as isize)) = (*LIST).current;
2030    OBJECT_STACK_DEPTH += 1;
2031
2032    // Dummy commands, overwritten in `sceGuEndObject`
2033    send_command_i(GeCommand::Base, 0);
2034    send_command_i(GeCommand::BJump, 0);
2035}
2036
2037/// End conditional rendering of object
2038#[allow(non_snake_case)]
2039#[no_mangle]
2040pub unsafe extern "C" fn sceGuEndObject() {
2041    // Rewrite commands from `sceGuBeginObject`
2042
2043    let current = (*LIST).current;
2044    (*LIST).current = *OBJECT_STACK.offset(OBJECT_STACK_DEPTH as isize - 1);
2045
2046    send_command_i(GeCommand::Base, (current as u32 >> 8) as i32 & 0xf0000);
2047    send_command_i(GeCommand::BJump, current as i32 & 0xffffff);
2048    (*LIST).current = current;
2049    OBJECT_STACK_DEPTH -= 1;
2050}
2051
2052/// Enable or disable GE state
2053///
2054/// Look at sceGuEnable() for a list of states
2055///
2056/// # Parameters
2057///
2058/// - `state`: Which state to change
2059/// - `status`: `1` to enable or `0` to disable the state
2060// TODO: bool for ABI?
2061#[allow(non_snake_case)]
2062#[no_mangle]
2063pub unsafe extern "C" fn sceGuSetStatus(state: GuState, status: i32) {
2064    if status != 0 {
2065        sceGuEnable(state);
2066    } else {
2067        sceGuDisable(state);
2068    }
2069}
2070
2071/// Get if state is currently enabled or disabled
2072///
2073/// # Parameters
2074///
2075/// - `state`: Which state to query about
2076///
2077/// # Return Value
2078///
2079/// Whether state is enabled or not
2080#[allow(non_snake_case)]
2081#[no_mangle]
2082pub unsafe extern "C" fn sceGuGetStatus(state: GuState) -> bool {
2083    let state = state as u32;
2084
2085    if state < 22 {
2086        return (STATES >> state) & 1 != 0;
2087    }
2088
2089    false
2090}
2091
2092/// Set the status on all 22 available states
2093///
2094/// # Parameters
2095///
2096/// - `status`: Bitmask (0-21) containing the status of all 22 states
2097#[allow(non_snake_case)]
2098#[no_mangle]
2099pub unsafe extern "C" fn sceGuSetAllStatus(status: i32) {
2100    for i in 0..22 {
2101        if (status >> i) & 1 != 0 {
2102            sceGuEnable(mem::transmute(i));
2103        } else {
2104            sceGuDisable(mem::transmute(i));
2105        }
2106    }
2107}
2108
2109/// Query status on all 22 available states
2110///
2111/// # Return Value
2112///
2113/// Status of all 22 states as a bitmask (0-21)
2114#[allow(non_snake_case)]
2115#[no_mangle]
2116pub unsafe extern "C" fn sceGuGetAllStatus() -> i32 {
2117    STATES as i32
2118}
2119
2120/// Enable GE state
2121///
2122/// # Parameters
2123///
2124/// - `state`: Which state to enable, one of `GuState`
2125#[allow(non_snake_case)]
2126#[no_mangle]
2127pub unsafe extern "C" fn sceGuEnable(state: GuState) {
2128    match state {
2129        GuState::AlphaTest => send_command_i(GeCommand::AlphaTestEnable, 1),
2130        GuState::DepthTest => send_command_i(GeCommand::ZTestEnable, 1),
2131        GuState::ScissorTest => {
2132            let context = &mut CONTEXTS[CURR_CONTEXT as usize];
2133            context.scissor_enable = 1;
2134            send_command_i(
2135                GeCommand::Scissor1,
2136                (context.scissor_start[1] << 10) | context.scissor_start[0],
2137            );
2138            send_command_i(
2139                GeCommand::Scissor2,
2140                (context.scissor_end[1] << 10) | context.scissor_end[0],
2141            );
2142        }
2143        GuState::StencilTest => send_command_i(GeCommand::StencilTestEnable, 1),
2144        GuState::Blend => send_command_i(GeCommand::AlphaBlendEnable, 1),
2145        GuState::CullFace => send_command_i(GeCommand::CullFaceEnable, 1),
2146        GuState::Dither => send_command_i(GeCommand::DitherEnable, 1),
2147        GuState::Fog => send_command_i(GeCommand::FogEnable, 1),
2148        GuState::ClipPlanes => send_command_i(GeCommand::DepthClampEnable, 1),
2149        GuState::Texture2D => send_command_i(GeCommand::TextureMapEnable, 1),
2150        GuState::Lighting => send_command_i(GeCommand::LightingEnable, 1),
2151        GuState::Light0 => send_command_i(GeCommand::LightEnable0, 1),
2152        GuState::Light1 => send_command_i(GeCommand::LightEnable1, 1),
2153        GuState::Light2 => send_command_i(GeCommand::LightEnable2, 1),
2154        GuState::Light3 => send_command_i(GeCommand::LightEnable3, 1),
2155        GuState::LineSmooth => send_command_i(GeCommand::AntiAliasEnable, 1),
2156        GuState::PatchCullFace => send_command_i(GeCommand::PatchCullEnable, 1),
2157        GuState::ColorTest => send_command_i(GeCommand::ColorTestEnable, 1),
2158        GuState::ColorLogicOp => send_command_i(GeCommand::LogicOpEnable, 1),
2159        GuState::FaceNormalReverse => send_command_i(GeCommand::ReverseNormal, 1),
2160        GuState::PatchFace => send_command_i(GeCommand::PatchFacing, 1),
2161        GuState::Fragment2X => {
2162            let context = &mut CONTEXTS[CURR_CONTEXT as usize];
2163            context.fragment_2x = 0x10000;
2164            send_command_i(GeCommand::TexFunc, 0x10000 | context.texture_function);
2165        }
2166    }
2167
2168    if (state as u32) < 22 {
2169        STATES |= 1 << state as u32
2170    }
2171}
2172
2173/// Disable GE state
2174///
2175/// # Parameters
2176///
2177/// - `state`: Which state to disable, one of `GuState`
2178#[allow(non_snake_case)]
2179#[no_mangle]
2180pub unsafe extern "C" fn sceGuDisable(state: GuState) {
2181    match state {
2182        GuState::AlphaTest => send_command_i(GeCommand::AlphaTestEnable, 0),
2183        GuState::DepthTest => send_command_i(GeCommand::ZTestEnable, 0),
2184        GuState::ScissorTest => {
2185            let context = &mut CONTEXTS[CURR_CONTEXT as usize];
2186            context.scissor_enable = 0;
2187            send_command_i(GeCommand::Scissor1, 0);
2188            send_command_i(
2189                GeCommand::Scissor2,
2190                ((DRAW_BUFFER.height - 1) << 10) | (DRAW_BUFFER.width - 1),
2191            );
2192        }
2193        GuState::StencilTest => send_command_i(GeCommand::StencilTestEnable, 0),
2194        GuState::Blend => send_command_i(GeCommand::AlphaBlendEnable, 0),
2195        GuState::CullFace => send_command_i(GeCommand::CullFaceEnable, 0),
2196        GuState::Dither => send_command_i(GeCommand::DitherEnable, 0),
2197        GuState::Fog => send_command_i(GeCommand::FogEnable, 0),
2198        GuState::ClipPlanes => send_command_i(GeCommand::DepthClampEnable, 0),
2199        GuState::Texture2D => send_command_i(GeCommand::TextureMapEnable, 0),
2200        GuState::Lighting => send_command_i(GeCommand::LightingEnable, 0),
2201        GuState::Light0 => send_command_i(GeCommand::LightEnable0, 0),
2202        GuState::Light1 => send_command_i(GeCommand::LightEnable1, 0),
2203        GuState::Light2 => send_command_i(GeCommand::LightEnable2, 0),
2204        GuState::Light3 => send_command_i(GeCommand::LightEnable3, 0),
2205        GuState::LineSmooth => send_command_i(GeCommand::AntiAliasEnable, 0),
2206        GuState::PatchCullFace => send_command_i(GeCommand::PatchCullEnable, 0),
2207        GuState::ColorTest => send_command_i(GeCommand::ColorTestEnable, 0),
2208        GuState::ColorLogicOp => send_command_i(GeCommand::LogicOpEnable, 0),
2209        GuState::FaceNormalReverse => send_command_i(GeCommand::ReverseNormal, 0),
2210        GuState::PatchFace => send_command_i(GeCommand::PatchFacing, 0),
2211        GuState::Fragment2X => {
2212            let context = &mut CONTEXTS[CURR_CONTEXT as usize];
2213            context.fragment_2x = 0;
2214            send_command_i(GeCommand::TexFunc, context.texture_function);
2215        }
2216    }
2217
2218    if (state as u32) < 22 {
2219        STATES &= !(1 << state as u32)
2220    }
2221}
2222
2223/// Set light parameters
2224///
2225/// # Parameters
2226///
2227/// - `light`: Light index
2228/// - `type`: Light type, one of `LightType`
2229/// - `components`: Light components, one or more of `LightComponent`
2230/// - `position`: Light position
2231// FIXME: light components seem to be a subset here.
2232#[allow(non_snake_case)]
2233#[no_mangle]
2234pub unsafe extern "C" fn sceGuLight(
2235    light: i32,
2236    type_: LightType,
2237    components: LightComponent,
2238    position: &ScePspFVector3,
2239) {
2240    let settings = &LIGHT_COMMANDS[light as usize];
2241
2242    send_command_f(settings.xpos, position.x);
2243    send_command_f(settings.ypos, position.y);
2244    send_command_f(settings.zpos, position.z);
2245
2246    let mut kind = 2;
2247    if components.bits() != 8 {
2248        kind = if components.bits() ^ 6 < 1 { 1 } else { 0 };
2249    }
2250
2251    send_command_i(settings.type_, ((type_ as i32 & 0x03) << 8) | kind);
2252}
2253
2254/// Set light attenuation
2255///
2256/// # Parameters
2257///
2258/// - `light`: Light index
2259/// - `atten0`: Constant attenuation factor
2260/// - `atten1`: Linear attenuation factor
2261/// - `atten2`: Quadratic attenuation factor
2262#[allow(non_snake_case)]
2263#[no_mangle]
2264pub unsafe extern "C" fn sceGuLightAtt(light: i32, atten0: f32, atten1: f32, atten2: f32) {
2265    let settings = &LIGHT_COMMANDS[light as usize];
2266    send_command_f(settings.constant, atten0);
2267    send_command_f(settings.linear, atten1);
2268    send_command_f(settings.quadratic, atten2);
2269}
2270
2271/// Set light color
2272///
2273/// # Parameters
2274///
2275/// - `light`: Light index
2276/// - `component`: Which component(s) to set
2277/// - `color`: Which color to use
2278#[allow(non_snake_case)]
2279#[no_mangle]
2280pub unsafe extern "C" fn sceGuLightColor(light: i32, component: LightComponent, color: u32) {
2281    let settings = &LIGHT_COMMANDS[light as usize];
2282
2283    // PSPSDK implements this as a jump table, probably for speed. Should we do
2284    // this too?
2285    //
2286    // TODO: Or maybe only certain combinations are valid?
2287
2288    if component.intersects(LightComponent::AMBIENT) {
2289        send_command_i(settings.ambient, (color & 0xffffff) as i32);
2290    }
2291
2292    if component.intersects(LightComponent::DIFFUSE) {
2293        send_command_i(settings.diffuse, (color & 0xffffff) as i32);
2294    }
2295
2296    if component.intersects(LightComponent::SPECULAR) {
2297        send_command_i(settings.specular, (color & 0xffffff) as i32);
2298    }
2299}
2300
2301/// Set light mode
2302///
2303/// # Parameters
2304///
2305/// - `mode`: Light mode to use
2306#[allow(non_snake_case)]
2307#[no_mangle]
2308pub unsafe extern "C" fn sceGuLightMode(mode: LightMode) {
2309    send_command_i(GeCommand::LightMode, mode as i32);
2310}
2311
2312/// Set spotlight parameters
2313///
2314/// # Parameters
2315///
2316/// - `light`: Light index
2317/// - `direction`: Spotlight direction
2318/// - `exponent`: Spotlight exponent
2319/// - `cutoff`: Spotlight cutoff angle (in radians)
2320#[allow(non_snake_case)]
2321#[no_mangle]
2322pub unsafe extern "C" fn sceGuLightSpot(
2323    light: i32,
2324    direction: &ScePspFVector3,
2325    exponent: f32,
2326    cutoff: f32,
2327) {
2328    let settings = &LIGHT_COMMANDS[light as usize];
2329
2330    send_command_f(settings.exponent, exponent);
2331    send_command_f(settings.cutoff, cutoff);
2332
2333    send_command_f(settings.xdir, direction.x);
2334    send_command_f(settings.ydir, direction.y);
2335    send_command_f(settings.zdir, direction.z);
2336}
2337
2338/// Clear current drawbuffer
2339///
2340/// # Parameters
2341///
2342/// - `flags`: Which part of the buffer to clear
2343#[allow(non_snake_case)]
2344#[no_mangle]
2345pub unsafe extern "C" fn sceGuClear(flags: ClearBuffer) {
2346    let context = &mut CONTEXTS[CURR_CONTEXT as usize];
2347
2348    struct Vertex {
2349        color: u32,
2350        x: u16,
2351        y: u16,
2352        z: u16,
2353        _pad: u16,
2354    }
2355
2356    let filter: u32 = match DRAW_BUFFER.pixel_size {
2357        DisplayPixelFormat::Psm5650 => context.clear_color & 0xffffff,
2358        DisplayPixelFormat::Psm5551 => {
2359            (context.clear_color & 0xffffff) | (context.clear_stencil << 31)
2360        }
2361        DisplayPixelFormat::Psm4444 => {
2362            (context.clear_color & 0xffffff) | (context.clear_stencil << 28)
2363        }
2364        DisplayPixelFormat::Psm8888 => {
2365            (context.clear_color & 0xffffff) | (context.clear_stencil << 24)
2366        }
2367    };
2368
2369    let vertices;
2370    let count;
2371
2372    if !flags.intersects(ClearBuffer::FAST_CLEAR_BIT) {
2373        vertices = sceGuGetMemory(2 * mem::size_of::<Vertex>() as i32) as *mut Vertex;
2374        count = 2;
2375
2376        (*vertices.offset(0)).color = 0;
2377        (*vertices.offset(0)).x = 0;
2378        (*vertices.offset(0)).y = 0;
2379        (*vertices.offset(0)).z = context.clear_depth as u16;
2380
2381        (*vertices.offset(1)).color = filter;
2382        (*vertices.offset(1)).x = DRAW_BUFFER.width as u16;
2383        (*vertices.offset(1)).y = DRAW_BUFFER.height as u16;
2384        (*vertices.offset(1)).z = context.clear_depth as u16;
2385    } else {
2386        count = ((DRAW_BUFFER.width + 63) / 64) * 2;
2387        vertices = sceGuGetMemory(count * core::mem::size_of::<Vertex>() as i32) as *mut Vertex;
2388
2389        let mut curr = vertices;
2390
2391        for i in 0..count {
2392            let j = i >> 1;
2393            let k = i & 1;
2394
2395            (*curr).color = filter;
2396            (*curr).x = (j + k) as u16 * 64;
2397            (*curr).y = (k * DRAW_BUFFER.height) as u16;
2398            (*curr).z = context.clear_depth as u16;
2399
2400            curr = curr.add(1);
2401        }
2402    }
2403
2404    {
2405        let relevant_flags = flags
2406            & (ClearBuffer::COLOR_BUFFER_BIT
2407                | ClearBuffer::STENCIL_BUFFER_BIT
2408                | ClearBuffer::DEPTH_BUFFER_BIT);
2409
2410        send_command_i(
2411            GeCommand::ClearMode,
2412            (relevant_flags.bits() << 8) as i32 | 0x01,
2413        );
2414    }
2415
2416    sceGuDrawArray(
2417        GuPrimitive::Sprites,
2418        VertexType::COLOR_8888 | VertexType::VERTEX_16BIT | VertexType::TRANSFORM_2D,
2419        count,
2420        null_mut(),
2421        vertices as *mut c_void,
2422    );
2423
2424    send_command_i(GeCommand::ClearMode, 0);
2425}
2426
2427/// Set the current clear-color
2428///
2429/// # Parameters
2430///
2431/// - `color`: Color to clear with
2432#[allow(non_snake_case)]
2433#[no_mangle]
2434pub unsafe extern "C" fn sceGuClearColor(color: u32) {
2435    CONTEXTS[CURR_CONTEXT as usize].clear_color = color;
2436}
2437
2438/// Set the current clear-depth
2439///
2440/// # Parameters
2441///
2442/// - `depth`: Set which depth to clear with (0x0000-0xffff)
2443// TODO: Can `depth` be u16 or does this cause issues with FFI ABI compatibility?
2444#[allow(non_snake_case)]
2445#[no_mangle]
2446pub unsafe extern "C" fn sceGuClearDepth(depth: u32) {
2447    CONTEXTS[CURR_CONTEXT as usize].clear_depth = depth;
2448}
2449
2450/// Set the current stencil clear value
2451///
2452/// # Parameters
2453///
2454/// - `stencil`: Set which stencil value to clear with (0-255)
2455// TODO: Can `stencil` be u8 or does this cause issues with FFI ABI compatibility?
2456#[allow(non_snake_case)]
2457#[no_mangle]
2458pub unsafe extern "C" fn sceGuClearStencil(stencil: u32) {
2459    CONTEXTS[CURR_CONTEXT as usize].clear_stencil = stencil;
2460}
2461
2462/// Set mask for which bits of the pixels to write
2463///
2464/// # Parameters
2465///
2466/// - `mask`: Which bits to filter against writes
2467#[allow(non_snake_case)]
2468#[no_mangle]
2469pub unsafe extern "C" fn sceGuPixelMask(mask: u32) {
2470    send_command_i(GeCommand::MaskRgb, mask as i32 & 0xffffff);
2471    send_command_i(GeCommand::MaskAlpha, (mask >> 24) as i32);
2472}
2473
2474/// Set current primitive color
2475///
2476/// # Parameters
2477///
2478/// - `color`: Which color to use (overridden by vertex colors)
2479#[allow(non_snake_case)]
2480#[no_mangle]
2481pub unsafe extern "C" fn sceGuColor(color: u32) {
2482    sceGuMaterial(
2483        LightComponent::AMBIENT | LightComponent::DIFFUSE | LightComponent::SPECULAR,
2484        color,
2485    );
2486}
2487
2488/// Set the color test function
2489///
2490/// The color test is only performed while `GuState::ColorTest` is enabled, e.g.
2491/// via `sceGuEnable`.
2492///
2493/// # Parameters
2494///
2495/// - `func`: Color test function
2496/// - `color`: Color to test against
2497/// - `mask`: Mask ANDed against both source and destination when testing
2498#[allow(non_snake_case)]
2499#[no_mangle]
2500pub unsafe extern "C" fn sceGuColorFunc(func: ColorFunc, color: u32, mask: u32) {
2501    send_command_i(GeCommand::ColorTest, func as i32 & 0x03);
2502    send_command_i(GeCommand::ColorRef, color as i32 & 0xffffff);
2503    send_command_i(GeCommand::ColorTestmask, mask as i32);
2504}
2505
2506/// Set which color components the material will receive
2507///
2508/// # Parameters
2509///
2510/// - `components`: Which component(s) to receive
2511#[allow(non_snake_case)]
2512#[no_mangle]
2513pub unsafe extern "C" fn sceGuColorMaterial(components: LightComponent) {
2514    send_command_i(GeCommand::MaterialUpdate, components.bits());
2515}
2516
2517/// Set the alpha test parameters
2518///
2519/// # Parameters
2520///
2521/// - `func`: Specifies the alpha comparison function.
2522/// - `value`: Specifies the reference value that incoming alpha values are compared to.
2523/// - `mask`: Specifies the mask that both values are ANDed with before comparison.
2524#[allow(non_snake_case)]
2525#[no_mangle]
2526pub unsafe extern "C" fn sceGuAlphaFunc(func: AlphaFunc, value: i32, mask: i32) {
2527    let arg = func as i32 | ((value & 0xff) << 8) | ((mask & 0xff) << 16);
2528    send_command_i(GeCommand::AlphaTest, arg);
2529}
2530
2531#[allow(non_snake_case)]
2532#[no_mangle]
2533pub unsafe extern "C" fn sceGuAmbient(color: u32) {
2534    send_command_i(GeCommand::AmbientColor, color as i32 & 0xffffff);
2535    send_command_i(GeCommand::AmbientAlpha, (color >> 24) as i32);
2536}
2537
2538#[allow(non_snake_case)]
2539#[no_mangle]
2540pub unsafe extern "C" fn sceGuAmbientColor(color: u32) {
2541    send_command_i(GeCommand::MaterialAmbient, color as i32 & 0xffffff);
2542    send_command_i(GeCommand::MaterialAlpha, (color >> 24) as i32);
2543}
2544
2545/// Set the blending mode
2546///
2547/// This is similar to `glBlendEquation` combined with `glBlendFunc` in OpenGL.
2548///
2549/// # Parameters
2550///
2551/// - `op`: Blending Operation
2552/// - `src`: Blending function for source operand
2553/// - `dest`: Blending function for dest operand
2554/// - `srcfix`: Fixed value for `BlendFactor::Fix` (source operand)
2555/// - `destfix`: Fixed value for `BlendFactor::Fix` (dest operand)
2556#[allow(non_snake_case)]
2557#[no_mangle]
2558pub unsafe extern "C" fn sceGuBlendFunc(
2559    op: BlendOp,
2560    src: BlendFactor,
2561    dest: BlendFactor,
2562    src_fix: u32,
2563    dest_fix: u32,
2564) {
2565    send_command_i(
2566        GeCommand::BlendMode,
2567        src as i32 | ((dest as i32) << 4) | ((op as i32) << 8),
2568    );
2569    send_command_i(GeCommand::BlendFixedA, src_fix as i32 & 0xffffff);
2570    send_command_i(GeCommand::BlendFixedB, dest_fix as i32 & 0xffffff);
2571}
2572
2573/// Set current primitive color, for specific light components.
2574///
2575/// # Parameters
2576///
2577/// - `components`: Which component(s) to set
2578/// - `color`: Color to set (*likely* overridden by vertex colors)
2579#[allow(non_snake_case)]
2580#[no_mangle]
2581pub unsafe extern "C" fn sceGuMaterial(components: LightComponent, color: u32) {
2582    if components.intersects(LightComponent::AMBIENT) {
2583        send_command_i(GeCommand::MaterialAmbient, color as i32 & 0xffffff);
2584        send_command_i(GeCommand::MaterialAlpha, (color >> 24) as i32);
2585    }
2586
2587    if components.intersects(LightComponent::DIFFUSE) {
2588        send_command_i(GeCommand::MaterialDiffuse, color as i32 & 0xffffff);
2589    }
2590
2591    if components.intersects(LightComponent::SPECULAR) {
2592        send_command_i(GeCommand::MaterialSpecular, color as i32 & 0xffffff);
2593    }
2594}
2595
2596// TODO: Needs documentation.
2597#[allow(non_snake_case)]
2598#[no_mangle]
2599pub unsafe extern "C" fn sceGuModelColor(emissive: u32, ambient: u32, diffuse: u32, specular: u32) {
2600    send_command_i(GeCommand::MaterialEmissive, emissive as i32 & 0xffffff);
2601    send_command_i(GeCommand::MaterialAmbient, ambient as i32 & 0xffffff);
2602    send_command_i(GeCommand::MaterialDiffuse, diffuse as i32 & 0xffffff);
2603    send_command_i(GeCommand::MaterialSpecular, specular as i32 & 0xffffff);
2604}
2605
2606/// Set stencil function and reference value for stencil testing
2607///
2608/// # Parameters
2609///
2610/// - `func`: Test function
2611/// - `ref_`: The reference value for the stencil test
2612/// - `mask`: Mask that is ANDed with both the reference value and stored
2613///   stencil value when the test is done
2614#[allow(non_snake_case)]
2615#[no_mangle]
2616pub unsafe extern "C" fn sceGuStencilFunc(func: StencilFunc, ref_: i32, mask: i32) {
2617    send_command_i(
2618        GeCommand::StencilTest,
2619        func as i32 | ((ref_ & 0xff) << 8) | ((mask & 0xff) << 16),
2620    );
2621}
2622
2623/// Set the stencil test actions
2624///
2625/// As stencil buffer shares memory with framebuffer alpha, resolution of the buffer
2626/// is directly in relation.
2627///
2628/// # Parameters
2629///
2630/// - `fail`: The action to take when the stencil test fails
2631/// - `zfail`: The action to take when the stencil test passes, but the depth test fails
2632/// - `zpass`: The action to take when both the stencil test and depth test pass
2633#[allow(non_snake_case)]
2634#[no_mangle]
2635pub unsafe extern "C" fn sceGuStencilOp(
2636    fail: StencilOperation,
2637    zfail: StencilOperation,
2638    zpass: StencilOperation,
2639) {
2640    send_command_i(
2641        GeCommand::StencilOp,
2642        fail as i32 | ((zfail as i32) << 8) | ((zpass as i32) << 16),
2643    );
2644}
2645
2646/// Set the specular power for the material
2647///
2648/// # Parameters
2649///
2650/// - `power`: Specular power
2651#[allow(non_snake_case)]
2652#[no_mangle]
2653pub unsafe extern "C" fn sceGuSpecular(power: f32) {
2654    send_command_f(GeCommand::MaterialSpecularCoef, power);
2655}
2656
2657/// Set the current face-order (for culling)
2658///
2659/// This only has effect when culling (`GuState::CullFace`) is enabled, e.g. via
2660/// `sceGuEnable`.
2661///
2662/// # Parameters
2663///
2664/// - `order`: Which order to use, one of `FrontFaceDirection`
2665#[allow(non_snake_case)]
2666#[no_mangle]
2667pub unsafe extern "C" fn sceGuFrontFace(order: FrontFaceDirection) {
2668    match order {
2669        FrontFaceDirection::CounterClockwise => send_command_i(GeCommand::Cull, 0),
2670        FrontFaceDirection::Clockwise => send_command_i(GeCommand::Cull, 1),
2671    }
2672}
2673
2674/// Set color logical operation
2675///
2676/// This operation only has effect if `GuState::ColorLogicOp` is enabled, e.g. via
2677/// `sceGuEnable`.
2678///
2679/// # Parameters
2680///
2681/// - `op`: Operation to execute
2682#[allow(non_snake_case)]
2683#[no_mangle]
2684pub unsafe extern "C" fn sceGuLogicalOp(op: LogicalOperation) {
2685    send_command_i(GeCommand::LogicOp, op as i32 & 0x0f);
2686}
2687
2688/// Set ordered pixel dither matrix
2689///
2690/// This dither matrix is only applied if `GuState::Dither` is enabled, e.g. via
2691/// `sceGuEnable`.
2692///
2693/// # Parameters
2694///
2695/// - `matrix`: Dither matrix
2696#[allow(non_snake_case)]
2697#[no_mangle]
2698pub unsafe extern "C" fn sceGuSetDither(matrix: &ScePspIMatrix4) {
2699    send_command_i(
2700        GeCommand::Dith0,
2701        (matrix.x.x & 0x0f)
2702            | ((matrix.x.y & 0x0f) << 4)
2703            | ((matrix.x.z & 0x0f) << 8)
2704            | ((matrix.x.w & 0x0f) << 12),
2705    );
2706
2707    send_command_i(
2708        GeCommand::Dith1,
2709        (matrix.y.x & 0x0f)
2710            | ((matrix.y.y & 0x0f) << 4)
2711            | ((matrix.y.z & 0x0f) << 8)
2712            | ((matrix.y.w & 0x0f) << 12),
2713    );
2714
2715    send_command_i(
2716        GeCommand::Dith2,
2717        (matrix.z.x & 0x0f)
2718            | ((matrix.z.y & 0x0f) << 4)
2719            | ((matrix.z.z & 0x0f) << 8)
2720            | ((matrix.z.w & 0x0f) << 12),
2721    );
2722
2723    send_command_i(
2724        GeCommand::Dith3,
2725        (matrix.w.x & 0x0f)
2726            | ((matrix.w.y & 0x0f) << 4)
2727            | ((matrix.w.z & 0x0f) << 8)
2728            | ((matrix.w.w & 0x0f) << 12),
2729    );
2730}
2731
2732/// Set how primitives are shaded
2733///
2734/// # Parameters
2735///
2736/// - `mode`: Which mode to use, one of `ShadingModel`.
2737#[allow(non_snake_case)]
2738#[no_mangle]
2739pub unsafe extern "C" fn sceGuShadeModel(mode: ShadingModel) {
2740    match mode {
2741        ShadingModel::Smooth => send_command_i(GeCommand::ShadeMode, 1),
2742        ShadingModel::Flat => send_command_i(GeCommand::ShadeMode, 0),
2743    }
2744}
2745
2746// TODO: Maybe add examples in documentation?
2747/// Image transfer using the GE
2748///
2749/// # Note
2750///
2751/// Data must be aligned to 1 quad word (16 bytes)
2752///
2753/// # Parameters
2754///
2755/// - `psm`: Pixel format for buffer
2756/// - `sx`: Source X
2757/// - `sy`: Source Y
2758/// - `width`: Image width
2759/// - `height`: Image height
2760/// - `srcw`: Source buffer width (block aligned)
2761/// - `src`: Source pointer
2762/// - `dx`: Destination X
2763/// - `dy`: Destination Y
2764/// - `destw`: Destination buffer width (block aligned)
2765/// - `dest`: Destination pointer
2766#[allow(non_snake_case)]
2767#[no_mangle]
2768pub unsafe extern "C" fn sceGuCopyImage(
2769    psm: DisplayPixelFormat,
2770    sx: i32,
2771    sy: i32,
2772    width: i32,
2773    height: i32,
2774    srcw: i32,
2775    src: *mut c_void,
2776    dx: i32,
2777    dy: i32,
2778    destw: i32,
2779    dest: *mut c_void,
2780) {
2781    send_command_i(GeCommand::TransferSrc, (src as i32) & 0xffffff);
2782    send_command_i(
2783        GeCommand::TransferSrcW,
2784        (((src as u32) & 0xff000000) >> 8) as i32 | srcw,
2785    );
2786    send_command_i(GeCommand::TransferSrcPos, (sy << 10) | sx);
2787    send_command_i(GeCommand::TransferDst, (dest as i32) & 0xffffff);
2788    send_command_i(
2789        GeCommand::TransferDstW,
2790        (((dest as u32) & 0xff000000) >> 8) as i32 | destw,
2791    );
2792    send_command_i(GeCommand::TransferDstPos, (dy << 10) | dx);
2793    send_command_i(GeCommand::TransferSize, ((height - 1) << 10) | (width - 1));
2794
2795    let is_32_bit_texel = if let DisplayPixelFormat::Psm8888 = psm {
2796        1
2797    } else {
2798        0
2799    };
2800
2801    send_command_i(GeCommand::TransferStart, is_32_bit_texel);
2802}
2803
2804/// Specify the texture environment color
2805///
2806/// This is used in the texture function when a constant color is needed.
2807///
2808/// See `sceGuTexFunc` for more information.
2809///
2810/// # Parameters
2811///
2812/// - `color`: Constant color (0x00BBGGRR)
2813#[allow(non_snake_case)]
2814#[no_mangle]
2815pub unsafe extern "C" fn sceGuTexEnvColor(color: u32) {
2816    send_command_i(GeCommand::TexEnvColor, color as i32 & 0xffffff);
2817}
2818
2819/// Set how the texture is filtered
2820///
2821/// # Parameters
2822///
2823/// - `min`: Minimizing filter
2824/// - `mag`: Magnifying filter
2825#[allow(non_snake_case)]
2826#[no_mangle]
2827pub unsafe extern "C" fn sceGuTexFilter(min: TextureFilter, mag: TextureFilter) {
2828    send_command_i(GeCommand::TexFilter, ((mag as i32) << 8) | (min as i32));
2829}
2830
2831/// Flush texture page-cache
2832///
2833/// Do this if you have copied/rendered into an area currently in the texture
2834/// cache.
2835#[allow(non_snake_case)]
2836#[no_mangle]
2837pub unsafe extern "C" fn sceGuTexFlush() {
2838    send_command_f(GeCommand::TexFlush, 0.0);
2839}
2840
2841/// Set how textures are applied
2842///
2843/// # Parameters
2844///
2845/// - `tfx`: Which apply-mode to use
2846/// - `tcc`: Which component-mode to use
2847#[allow(non_snake_case)]
2848#[no_mangle]
2849pub unsafe extern "C" fn sceGuTexFunc(tfx: TextureEffect, tcc: TextureColorComponent) {
2850    let context = &mut CONTEXTS[CURR_CONTEXT as usize];
2851    context.texture_function = (((tcc as u32) << 8) | (tfx as u32)) as i32;
2852    send_command_i(
2853        GeCommand::TexFunc,
2854        (((tcc as u32) << 8) | (tfx as u32) | context.fragment_2x as u32) as i32,
2855    );
2856}
2857
2858/// Set current texturemap
2859///
2860/// Textures may reside in main RAM, but it has a huge speed-penalty. Swizzle textures
2861/// to get maximum speed.
2862///
2863/// # Note
2864///
2865/// Data must be aligned to 1 quad word (16 bytes)
2866///
2867/// # Parameters
2868///
2869/// - `mipmap`: Mipmap level
2870/// - `width`: Width of texture map (must be a power of 2)
2871/// - `height`: Height of texture map (must be a power of 2)
2872/// - `tbw`: Texture Buffer Width (block-aligned). For `PixelFormat::8888`, this
2873///    seems to have to be a multiple of 4. This value indicates the actual
2874///    width of the image, not the width of the texture map.
2875/// - `tbp`: Texture buffer pointer (16 byte aligned)
2876///
2877/// # `width` vs `tbw`
2878///
2879/// The parameters `height` and `width` indicate the size of the texture map. It
2880/// is possible to pass in oddly sized textures, however, using the `tbw`
2881/// parameter, as long as the true width is divisible by (... block size? The
2882/// block size seems to be 4 for `PixelFormat::8888`).
2883///
2884/// As an example, say you have a 340x340 image. This image can be passed in
2885/// like so:
2886///
2887/// ```ignore
2888/// let image_data = ...; // Some 16-byte aligned source.
2889/// sceGuTexImage(MipmapLevel::None, 512, 512, 340, data);
2890/// ```
2891///
2892/// This will generate a 512x512 pixel texture map, with the remaining horizontal
2893/// space being filled with the original texture repeating. The remaining
2894/// vertical space will overflow into the data past the input buffer, which may
2895/// appear as garbage data. This is not a problem as the UV coordinates on the
2896/// triangles can be crafted to stay within the image bounds, both vertically and
2897/// horizontally.
2898#[allow(non_snake_case)]
2899#[no_mangle]
2900pub unsafe extern "C" fn sceGuTexImage(
2901    mipmap: MipmapLevel,
2902    width: i32,
2903    height: i32,
2904    tbw: i32,
2905    tbp: *const c_void,
2906) {
2907    use core::intrinsics::ctlz;
2908
2909    const TBP_CMD_TBL: [GeCommand; 8] = [
2910        GeCommand::TexAddr0,
2911        GeCommand::TexAddr1,
2912        GeCommand::TexAddr2,
2913        GeCommand::TexAddr3,
2914        GeCommand::TexAddr4,
2915        GeCommand::TexAddr5,
2916        GeCommand::TexAddr6,
2917        GeCommand::TexAddr7,
2918    ];
2919
2920    const TBW_CMD_TBL: [GeCommand; 8] = [
2921        GeCommand::TexBufWidth0,
2922        GeCommand::TexBufWidth1,
2923        GeCommand::TexBufWidth2,
2924        GeCommand::TexBufWidth3,
2925        GeCommand::TexBufWidth4,
2926        GeCommand::TexBufWidth5,
2927        GeCommand::TexBufWidth6,
2928        GeCommand::TexBufWidth7,
2929    ];
2930
2931    const TSIZE_CMD_TBL: [GeCommand; 8] = [
2932        GeCommand::TexSize0,
2933        GeCommand::TexSize1,
2934        GeCommand::TexSize2,
2935        GeCommand::TexSize3,
2936        GeCommand::TexSize4,
2937        GeCommand::TexSize5,
2938        GeCommand::TexSize6,
2939        GeCommand::TexSize7,
2940    ];
2941
2942    send_command_i(TBP_CMD_TBL[mipmap as usize], (tbp as i32) & 0xffffff);
2943    send_command_i(
2944        TBW_CMD_TBL[mipmap as usize],
2945        ((tbp as u32 >> 8) as i32 & 0x0f0000) | tbw,
2946    );
2947    send_command_i(
2948        TSIZE_CMD_TBL[mipmap as usize],
2949        (((31 - ctlz(height & 0x3ff)) << 8) | (31 - ctlz(width & 0x3ff))) as i32,
2950    );
2951    sceGuTexFlush();
2952}
2953
2954/// Set texture-level mode (mipmapping)
2955///
2956/// # Parameters
2957///
2958/// - `mode`: Which mode to use, one of TextureLevelMode
2959/// - `bias`: Which mipmap bias to use
2960#[allow(non_snake_case)]
2961#[no_mangle]
2962pub unsafe extern "C" fn sceGuTexLevelMode(mode: TextureLevelMode, bias: f32) {
2963    // Linker error if this is not here.
2964    #[no_mangle]
2965    #[cfg(target_os = "psp")]
2966    #[allow(deprecated)]
2967    unsafe extern "C" fn truncf(mut x: f32) -> f32 {
2968        core::arch::asm!("cvt.w.s {0}, {0}", inout(freg) x);
2969        x
2970    }
2971
2972    let mut offset = core::intrinsics::truncf32(bias * 16.0) as i32;
2973
2974    // PSPSDK: mip map bias?
2975    if offset >= 128 {
2976        offset = 128
2977    } else if offset < -128 {
2978        offset = -128;
2979    }
2980
2981    send_command_i(GeCommand::TexLevel, ((offset as i32) << 16) | mode as i32);
2982}
2983
2984/// Set the texture-mapping mode
2985///
2986/// # Parameters
2987///
2988/// - `mode`: Which mode to use
2989/// - `a1`: Unknown
2990/// - `a2`: Unknown
2991#[allow(non_snake_case)]
2992#[no_mangle]
2993pub unsafe extern "C" fn sceGuTexMapMode(mode: TextureMapMode, a1: u32, a2: u32) {
2994    let context = &mut CONTEXTS[CURR_CONTEXT as usize];
2995    context.texture_map_mode = mode;
2996    send_command_i(
2997        GeCommand::TexMapMode,
2998        ((context.texture_proj_map_mode as i32) << 8) | mode as i32,
2999    );
3000    send_command_i(GeCommand::TexShadeLs, ((a2 << 8) | (a1 & 0x03)) as i32);
3001}
3002
3003/// Set texture-mode parameters
3004///
3005/// # Parameters
3006///
3007/// - `tpsm`: Which texture format to use
3008/// - `maxmips`: Number of mipmaps to use (0-7)
3009/// - `a2`: Unknown, set to 0
3010/// - `swizzle`: `1` to swizzle texture-reads.
3011// TODO: Are boolean parameters ABI compatibile with FFI i32 parameters?
3012// TODO: Better documentation for `maxmips`. What does it do? Maybe it should be
3013//       of type `MipmapLevel`?
3014#[allow(non_snake_case)]
3015#[no_mangle]
3016pub unsafe extern "C" fn sceGuTexMode(
3017    tpsm: TexturePixelFormat,
3018    maxmips: i32,
3019    a2: i32,
3020    swizzle: i32,
3021) {
3022    CONTEXTS[CURR_CONTEXT as usize].texture_mode = tpsm;
3023
3024    send_command_i(GeCommand::TexMode, (maxmips << 16) | (a2 << 8) | swizzle);
3025
3026    send_command_i(GeCommand::TexFormat, tpsm as i32);
3027    sceGuTexFlush();
3028}
3029
3030/// Set texture offset
3031///
3032/// # Note
3033///
3034/// Only used by the 3D T&L pipe, renders done with `VertexType::TRANSFORM_2D`
3035/// are not affected by this.
3036///
3037/// # Parameters
3038///
3039/// - `u`: Offset to add to the U coordinate
3040/// - `v`: Offset to add to the V coordinate
3041#[allow(non_snake_case)]
3042#[no_mangle]
3043pub unsafe extern "C" fn sceGuTexOffset(u: f32, v: f32) {
3044    send_command_f(GeCommand::TexOffsetU, u);
3045    send_command_f(GeCommand::TexOffsetV, v);
3046}
3047
3048/// Set texture projection-map mode
3049///
3050/// # Parameters
3051///
3052/// - `mode`: Which mode to use
3053#[allow(non_snake_case)]
3054#[no_mangle]
3055pub unsafe extern "C" fn sceGuTexProjMapMode(mode: TextureProjectionMapMode) {
3056    let context = &mut CONTEXTS[CURR_CONTEXT as usize];
3057    context.texture_proj_map_mode = mode;
3058    send_command_i(
3059        GeCommand::TexMapMode,
3060        ((mode as i32) << 8) | context.texture_map_mode as i32,
3061    );
3062}
3063
3064/// Set texture scale
3065///
3066/// # Note
3067///
3068/// Only used by the 3D T&L pipe, renders ton with `VertexType::TRANSFORM_2D`
3069/// are not affected by this.
3070///
3071/// # Parameters
3072///
3073/// - `u`: Scalar to multiply U coordinate with
3074/// - `v`: Scalar to multiply V coordinate with
3075#[allow(non_snake_case)]
3076#[no_mangle]
3077pub unsafe extern "C" fn sceGuTexScale(u: f32, v: f32) {
3078    send_command_f(GeCommand::TexScaleU, u);
3079    send_command_f(GeCommand::TexScaleV, v);
3080}
3081
3082#[allow(non_snake_case)]
3083#[no_mangle]
3084pub unsafe extern "C" fn sceGuTexSlope(slope: f32) {
3085    send_command_f(GeCommand::TexLodSlope, slope);
3086}
3087
3088/// Synchronize rendering pipeline with image upload.
3089///
3090/// This will stall the rendering pipeline until the current image upload initiated by
3091/// `sceGuCopyImage` has completed.
3092#[allow(non_snake_case)]
3093#[no_mangle]
3094pub unsafe extern "C" fn sceGuTexSync() {
3095    send_command_i(GeCommand::TexSync, 0);
3096}
3097
3098/// Set if the texture should repeat or clamp
3099///
3100/// Available modes are:
3101///
3102/// # Parameters
3103///
3104/// - `u`: Wrap-mode for the U direction
3105/// - `v`: Wrap-mode for the V direction
3106#[allow(non_snake_case)]
3107#[no_mangle]
3108pub unsafe extern "C" fn sceGuTexWrap(u: GuTexWrapMode, v: GuTexWrapMode) {
3109    send_command_i(GeCommand::TexWrap, ((v as i32) << 8) | u as i32);
3110}
3111
3112/// Upload CLUT (Color Lookup Table)
3113///
3114/// # Note
3115///
3116/// Data must be aligned to 1 quad word (16 bytes)
3117///
3118/// # Parameters
3119///
3120/// - `num_blocks`: How many blocks of 8 entries to upload (32*8 is 256 colors)
3121/// - `cbp`: Pointer to palette (16 byte aligned)
3122#[allow(non_snake_case)]
3123#[no_mangle]
3124pub unsafe extern "C" fn sceGuClutLoad(num_blocks: i32, cbp: *const c_void) {
3125    send_command_i(GeCommand::ClutAddr, (cbp as i32) & 0xffffff);
3126    send_command_i(
3127        GeCommand::ClutAddrUpper,
3128        ((cbp as u32) >> 8) as i32 & 0xf0000,
3129    );
3130    send_command_i(GeCommand::LoadClut, num_blocks);
3131}
3132
3133/// CLUT palette pixel formats.
3134///
3135/// This is the pixel format for the input palette when setting up a CLUT.
3136#[repr(u32)]
3137pub enum ClutPixelFormat {
3138    /// Hicolor, 16-bit, RGB 5:6:5
3139    Psm5650 = 0,
3140    /// Hicolor, 16-bit, RGBA 5:5:5:1
3141    Psm5551 = 1,
3142    /// Hicolor, 16-bit, RGBA 4:4:4:4
3143    Psm4444 = 2,
3144    /// Truecolor, 32-bit, RGBA 8:8:8:8
3145    Psm8888 = 3,
3146}
3147
3148/// Set current CLUT mode
3149///
3150/// # Parameters
3151///
3152/// - `cpsm`: Which pixel format to use for the palette
3153/// - `shift`: Shifts color index by that many bits to the right
3154/// - `mask`: Masks the color index with this bitmask after the shift (0-0xFF)
3155/// - `a3`: Unknown, set to 0
3156#[allow(non_snake_case)]
3157#[no_mangle]
3158pub unsafe extern "C" fn sceGuClutMode(cpsm: ClutPixelFormat, shift: u32, mask: u32, a3: u32) {
3159    let arg = ((cpsm as u32) | (shift << 2) | (mask << 8) | (a3 << 16)) as i32;
3160    send_command_i(GeCommand::ClutFormat, arg);
3161}
3162
3163/// Set virtual coordinate offset
3164///
3165/// The PSP has a virtual coordinate-space of 4096x4096, this controls where
3166/// rendering is performed.
3167///
3168/// # Example
3169///
3170/// Center the virtual coordinate range:
3171///
3172/// ```no_run
3173/// # use psp::sys::sceGuOffset;
3174/// sceGuOffset(2048 - (480 / 2), 2048 - (272 / 2));
3175/// ```
3176///
3177/// # Parameters
3178///
3179/// - `x`: Offset (0-4095)
3180/// - `y`: Offset (0-4095)
3181#[allow(non_snake_case)]
3182#[no_mangle]
3183pub unsafe extern "C" fn sceGuOffset(x: u32, y: u32) {
3184    send_command_i(GeCommand::OffsetX, (x << 4) as i32);
3185    send_command_i(GeCommand::OffsetY, (y << 4) as i32);
3186}
3187
3188/// Set what to scissor within the current viewport
3189///
3190/// Note that scissoring is only performed if the custom scissoring
3191/// (`GuState::ScissorTest`) is enabled, e.g. via `sceGuEnable`.
3192///
3193/// # Parameters
3194///
3195/// - `x`: Left of scissor region
3196/// - `y`: Top of scissor region
3197/// - `w`: Width of scissor region
3198/// - `h`: Height of scissor region
3199#[allow(non_snake_case)]
3200#[no_mangle]
3201pub unsafe extern "C" fn sceGuScissor(x: i32, y: i32, w: i32, h: i32) {
3202    let context = &mut CONTEXTS[CURR_CONTEXT as usize];
3203
3204    context.scissor_start = [x, y];
3205    context.scissor_end = [w - 1, h - 1];
3206
3207    if context.scissor_enable != 0 {
3208        send_command_i(
3209            GeCommand::Scissor1,
3210            (context.scissor_start[1] << 10) | context.scissor_start[0],
3211        );
3212        send_command_i(
3213            GeCommand::Scissor2,
3214            (context.scissor_end[1] << 10) | context.scissor_end[0],
3215        );
3216    }
3217}
3218
3219/// Set current viewport
3220///
3221/// # Example
3222///
3223/// Setup a viewport of size (480,272) with origin at (2048,2048)
3224///
3225/// ```no_run
3226/// # use psp::sys::sceGuViewport;
3227/// sceGuViewport(2048, 2048, 480, 272);
3228/// ```
3229///
3230/// # Parameters
3231///
3232/// - `cx`: Center for horizontal viewport
3233/// - `cy`: Center for vertical viewport
3234/// - `width`: Width of viewport
3235/// - `height`: Height of viewport
3236#[allow(non_snake_case)]
3237#[no_mangle]
3238pub unsafe extern "C" fn sceGuViewport(cx: i32, cy: i32, width: i32, height: i32) {
3239    send_command_f(GeCommand::ViewportXScale, (width >> 1) as f32);
3240    send_command_f(GeCommand::ViewportYScale, ((-height) >> 1) as f32);
3241    send_command_f(GeCommand::ViewportXCenter, cx as f32);
3242    send_command_f(GeCommand::ViewportYCenter, cy as f32);
3243}
3244
3245/// Draw bezier surface
3246///
3247/// # Parameters
3248///
3249/// - `vtype`: Vertex type
3250/// - `ucount`: Number of vertices used in the U direction
3251/// - `vcount`: Number of vertices used in the V direction
3252/// - `indices`: Pointer to index buffer
3253/// - `vertices`: Pointer to vertex buffer
3254#[allow(non_snake_case)]
3255#[no_mangle]
3256pub unsafe extern "C" fn sceGuDrawBezier(
3257    v_type: VertexType,
3258    u_count: i32,
3259    v_count: i32,
3260    indices: *const c_void,
3261    vertices: *const c_void,
3262) {
3263    if !v_type.is_empty() {
3264        send_command_i(GeCommand::VertexType, v_type.bits());
3265    }
3266
3267    if !indices.is_null() {
3268        send_command_i(GeCommand::Base, ((indices as u32) >> 8) as i32 & 0xf0000);
3269        send_command_i(GeCommand::Iaddr, (indices as i32) & 0xffffff);
3270    }
3271
3272    if !vertices.is_null() {
3273        send_command_i(GeCommand::Base, ((vertices as u32) >> 8) as i32 & 0xf0000);
3274        send_command_i(GeCommand::Vaddr, (vertices as i32) & 0xffffff);
3275    }
3276
3277    send_command_i(GeCommand::Bezier, (v_count << 8) | u_count);
3278}
3279
3280/// Set dividing for patches (beziers and splines)
3281///
3282/// # Parameters
3283///
3284/// - `ulevel`: Number of division on u direction
3285/// - `vlevel`: Number of division on v direction
3286#[allow(non_snake_case)]
3287#[no_mangle]
3288pub unsafe extern "C" fn sceGuPatchDivide(ulevel: u32, vlevel: u32) {
3289    send_command_i(GeCommand::PatchDivision, ((vlevel << 8) | ulevel) as i32);
3290}
3291
3292#[allow(non_snake_case)]
3293#[no_mangle]
3294pub unsafe extern "C" fn sceGuPatchFrontFace(a0: u32) {
3295    send_command_i(GeCommand::PatchFacing, a0 as i32);
3296}
3297
3298/// Set primitive for patches (beziers and splines)
3299///
3300/// # Parameters
3301///
3302/// - `prim`: Desired primitive type
3303#[allow(non_snake_case)]
3304#[no_mangle]
3305pub unsafe extern "C" fn sceGuPatchPrim(prim: PatchPrimitive) {
3306    match prim {
3307        PatchPrimitive::Points => send_command_i(GeCommand::PatchPrimitive, 2),
3308        PatchPrimitive::LineStrip => send_command_i(GeCommand::PatchPrimitive, 1),
3309        PatchPrimitive::TriangleStrip => send_command_i(GeCommand::PatchPrimitive, 0),
3310    }
3311}
3312
3313#[allow(non_snake_case)]
3314#[no_mangle]
3315pub unsafe extern "C" fn sceGuDrawSpline(
3316    v_type: VertexType,
3317    u_count: i32,
3318    v_count: i32,
3319    u_edge: i32,
3320    v_edge: i32,
3321    indices: *const c_void,
3322    vertices: *const c_void,
3323) {
3324    if !v_type.is_empty() {
3325        send_command_i(GeCommand::VertexType, v_type.bits());
3326    }
3327
3328    if !indices.is_null() {
3329        send_command_i(GeCommand::Base, ((indices as u32) >> 8) as i32 & 0xf0000);
3330        send_command_i(GeCommand::Iaddr, (indices as i32) & 0xffffff);
3331    }
3332
3333    if !vertices.is_null() {
3334        send_command_i(GeCommand::Base, ((vertices as u32) >> 8) as i32 & 0xf0000);
3335        send_command_i(GeCommand::Vaddr, (vertices as i32) & 0xffffff);
3336    }
3337
3338    send_command_i(
3339        GeCommand::Spline,
3340        (v_edge << 18) | (u_edge << 16) | (v_count << 8) | u_count,
3341    );
3342}
3343
3344/// Set transform matrices
3345///
3346/// # Parameters
3347///
3348/// - `type`: Which matrix-type to set
3349/// - `matrix`: Matrix to load
3350#[allow(non_snake_case)]
3351#[no_mangle]
3352pub unsafe extern "C" fn sceGuSetMatrix(type_: MatrixMode, matrix: &ScePspFMatrix4) {
3353    let fmatrix = matrix as *const _ as *const f32;
3354
3355    match type_ {
3356        MatrixMode::Projection => {
3357            send_command_f(GeCommand::ProjMatrixNumber, 0.0);
3358            for i in 0..16 {
3359                send_command_f(GeCommand::ProjMatrixData, *fmatrix.offset(i));
3360            }
3361        }
3362
3363        MatrixMode::View => {
3364            send_command_f(GeCommand::ViewMatrixNumber, 0.0);
3365            for i in 0..4 {
3366                for j in 0..3 {
3367                    send_command_f(GeCommand::ViewMatrixData, *fmatrix.offset(j + i * 4));
3368                }
3369            }
3370        }
3371
3372        MatrixMode::Model => {
3373            send_command_f(GeCommand::WorldMatrixNumber, 0.0);
3374            for i in 0..4 {
3375                for j in 0..3 {
3376                    send_command_f(GeCommand::WorldMatrixData, *fmatrix.offset(j + i * 4));
3377                }
3378            }
3379        }
3380
3381        MatrixMode::Texture => {
3382            send_command_f(GeCommand::TGenMatrixNumber, 0.0);
3383            for i in 0..4 {
3384                for j in 0..3 {
3385                    send_command_f(GeCommand::TGenMatrixData, *fmatrix.offset(j + i * 4));
3386                }
3387            }
3388        }
3389    }
3390}
3391
3392/// Specify skinning matrix entry
3393///
3394/// To enable vertex skinning, use `VertexType::WEIGHTSn`, where n is between
3395/// 1-8, and pass an applicable `VertexType::WEIGHT_?BIT` declaration. This will
3396/// change the amount of weights passed in the vertex array, and by setting the
3397/// skinning matrices, you will multiply each vertex every weight and vertex
3398/// passed.
3399///
3400/// Please see `VertexType` for vertex format information.
3401///
3402/// # Parameters
3403///
3404/// - `index`: Skinning matrix index (0-7)
3405/// - `matrix`: Matrix to set
3406#[allow(non_snake_case)]
3407#[no_mangle]
3408pub unsafe extern "C" fn sceGuBoneMatrix(index: u32, matrix: &ScePspFMatrix4) {
3409    send_command_i(GeCommand::BoneMatrixNumber, index as i32 * 12); // 3 * 4 matrix
3410
3411    send_command_f(GeCommand::BoneMatrixData, matrix.x.x);
3412    send_command_f(GeCommand::BoneMatrixData, matrix.x.y);
3413    send_command_f(GeCommand::BoneMatrixData, matrix.x.z);
3414
3415    send_command_f(GeCommand::BoneMatrixData, matrix.y.x);
3416    send_command_f(GeCommand::BoneMatrixData, matrix.y.y);
3417    send_command_f(GeCommand::BoneMatrixData, matrix.y.z);
3418
3419    send_command_f(GeCommand::BoneMatrixData, matrix.z.x);
3420    send_command_f(GeCommand::BoneMatrixData, matrix.z.y);
3421    send_command_f(GeCommand::BoneMatrixData, matrix.z.z);
3422
3423    send_command_f(GeCommand::BoneMatrixData, matrix.w.x);
3424    send_command_f(GeCommand::BoneMatrixData, matrix.w.y);
3425    send_command_f(GeCommand::BoneMatrixData, matrix.w.z);
3426}
3427
3428/// Specify morph weight entry
3429///
3430/// To enable vertex morphing, use `VertexType::VERTICESn`, where n is between
3431/// 1-8. This will change the amount of vertices passed in the vertex array,
3432/// and by setting the morph weights for every vertex entry in the array,
3433/// you can blend between them.
3434///
3435/// Please see `VertexType` for vertex format information.
3436///
3437/// # Parameters
3438///
3439/// - `index`: Morph weight index (0-7)
3440/// - `weight`: Weight to set
3441#[allow(non_snake_case)]
3442#[no_mangle]
3443pub unsafe extern "C" fn sceGuMorphWeight(index: i32, weight: f32) {
3444    let cmd = match index {
3445        0 => GeCommand::MorphWeight0,
3446        1 => GeCommand::MorphWeight1,
3447        2 => GeCommand::MorphWeight2,
3448        3 => GeCommand::MorphWeight3,
3449        4 => GeCommand::MorphWeight4,
3450        5 => GeCommand::MorphWeight5,
3451        6 => GeCommand::MorphWeight6,
3452        7 => GeCommand::MorphWeight7,
3453        _ => core::intrinsics::unreachable(),
3454    };
3455
3456    send_command_f(cmd, weight);
3457}
3458
3459#[allow(non_snake_case)]
3460#[no_mangle]
3461pub unsafe extern "C" fn sceGuDrawArrayN(
3462    primitive_type: GuPrimitive,
3463    v_type: VertexType,
3464    count: i32,
3465    a3: i32,
3466    indices: *const c_void,
3467    vertices: *const c_void,
3468) {
3469    if !v_type.is_empty() {
3470        send_command_i(GeCommand::VertexType, v_type.bits());
3471    }
3472
3473    if !indices.is_null() {
3474        send_command_i(GeCommand::Base, ((indices as u32) >> 8) as i32 & 0xf0000);
3475        send_command_i(GeCommand::Iaddr, indices as i32 & 0xffffff);
3476    }
3477
3478    if !vertices.is_null() {
3479        send_command_i(GeCommand::Base, ((vertices as u32) >> 8) as i32 & 0xf0000);
3480        send_command_i(GeCommand::Vaddr, vertices as i32 & 0xffffff);
3481    }
3482
3483    if a3 > 0 {
3484        // PSPSDK: TODO: not sure about this loop, might be off. review
3485        for _ in 1..a3 {
3486            send_command_i(GeCommand::Prim, ((primitive_type as i32) << 16) | count);
3487        }
3488
3489        send_command_i_stall(GeCommand::Prim, ((primitive_type as i32) << 16) | count);
3490    }
3491}
3492
3493static mut CHAR_BUFFER_USED: u32 = 0;
3494static mut CHAR_BUFFER: [DebugCharStruct; 2048] = [DebugCharStruct {
3495    x: 0,
3496    y: 0,
3497    color: 0,
3498    character: b'\0',
3499    unused: [0, 0, 0],
3500}; 2048];
3501static FONT: [u8; 768] = *include_bytes!("./debugfont.bin");
3502
3503#[repr(C, packed)]
3504#[derive(Copy, Clone)]
3505struct DebugCharStruct {
3506    x: i32,
3507    y: i32,
3508    color: u32,
3509    character: u8,
3510    unused: [u8; 3],
3511}
3512
3513/// Add characters to an internal buffer for later printing with sceGuDebugFlush
3514///
3515/// # Parameters
3516///
3517/// - `x`: Horizontal start position
3518/// - `y`: Vertical start position
3519/// - `color`: Text color, ABGR
3520/// - `msg`: C-style string
3521#[allow(non_snake_case)]
3522#[no_mangle]
3523pub unsafe extern "C" fn sceGuDebugPrint(x: i32, mut y: i32, mut color: u32, mut msg: *const u8) {
3524    let mut cur_char: u8;
3525    let mut uVar1: u32;
3526    let iVar2: i32;
3527    let mut cur_x: i32;
3528    let mut char_struct_ptr: *mut DebugCharStruct =
3529        addr_of_mut!(CHAR_BUFFER).cast::<DebugCharStruct>();
3530
3531    let mut i = CHAR_BUFFER_USED;
3532    if i >= 0x3ff {
3533        return;
3534    }
3535    uVar1 = color >> 8 & 0xff;
3536    let uVar3 = color >> 16 & 0xff;
3537    let iVar4 = (uVar3 >> 3) as i32;
3538    cur_x = x;
3539    match DRAW_BUFFER.pixel_size {
3540        DisplayPixelFormat::Psm5650 => {
3541            iVar2 = (uVar1 as i32) >> 2;
3542            uVar1 = (iVar4 as u32) << 0xb;
3543            uVar1 = (uVar1 | iVar2 as u32) << 5;
3544            color = (color & 0xff) >> 3;
3545            color |= uVar1;
3546        }
3547        DisplayPixelFormat::Psm5551 => {
3548            iVar2 = (uVar1 >> 3) as i32;
3549            uVar1 = ((color >> 24) >> 7) << 0xf | (iVar4 as u32) << 10;
3550            uVar1 = (uVar1 | iVar2 as u32) << 5;
3551            color = (color & 0xff) >> 3;
3552            color |= uVar1;
3553        }
3554        DisplayPixelFormat::Psm8888 => {}
3555        DisplayPixelFormat::Psm4444 => {
3556            uVar1 = ((color >> 0x18) >> 4) << 0xc | (uVar3 >> 4) << 8 | (uVar1 >> 4) << 4;
3557            color &= 0xff >> 4;
3558            color |= uVar1;
3559        }
3560    }
3561    cur_char = *msg;
3562    while cur_char != b'\0' {
3563        if cur_char == b'\n' {
3564            y += 8;
3565            cur_x = x;
3566        } else {
3567            (*char_struct_ptr).x = cur_x;
3568            i += 1;
3569            (*char_struct_ptr).character = cur_char - 0x20;
3570            (*char_struct_ptr).y = y;
3571            (*char_struct_ptr).color = color;
3572            char_struct_ptr = (char_struct_ptr as u32 + 16) as *mut DebugCharStruct;
3573            cur_x += 8;
3574        }
3575        msg = msg.add(1);
3576        cur_char = *msg;
3577    }
3578    CHAR_BUFFER_USED = i;
3579}
3580
3581/// Flush character buffer created by sceGuDebugPrint to the draw buffer
3582#[allow(non_snake_case)]
3583#[no_mangle]
3584pub unsafe extern "C" fn sceGuDebugFlush() {
3585    let edram_address = GE_EDRAM_ADDRESS;
3586    let mut pixel_size: DisplayPixelFormat;
3587    let mut frame_width: i32;
3588    let mut frame_buffer: *mut c_void;
3589    let draw_buffer_height = DRAW_BUFFER.height;
3590    let mut char_index: i32;
3591    let mut pos: i32;
3592    let mut x_pixel_counter: i32;
3593    let mut glyph_pos: u32 = 0;
3594    let mut color: u32;
3595    let mut font_glyph: u32 = 0;
3596    let mut y_pixel_counter: i32;
3597    let mut x: i32;
3598    let mut char_buffer_used = CHAR_BUFFER_USED;
3599    let mut y: i32;
3600    let mut char_struct_ptr: *mut DebugCharStruct =
3601        addr_of_mut!(CHAR_BUFFER).cast::<DebugCharStruct>();
3602
3603    if char_buffer_used != 0 {
3604        loop {
3605            frame_buffer = DRAW_BUFFER.frame_buffer;
3606            frame_width = DRAW_BUFFER.frame_width;
3607            pixel_size = DRAW_BUFFER.pixel_size;
3608            y = (*char_struct_ptr).y;
3609            x = (*char_struct_ptr).x;
3610            if (y + 7 < draw_buffer_height)
3611                && (((x + 7 < DRAW_BUFFER.width) as i32 & !y >> 0x1f) != 0)
3612                && -1 < x
3613            {
3614                color = (*char_struct_ptr).color;
3615                char_index = ((*char_struct_ptr).character) as i32 * 8;
3616                y_pixel_counter = 0;
3617                loop {
3618                    if y_pixel_counter == 0 {
3619                        font_glyph =
3620                            *(((&FONT as *const _ as u32) + char_index as u32) as *const u32);
3621                        glyph_pos = 1;
3622                    } else if y_pixel_counter == 4 {
3623                        font_glyph =
3624                            *(((&FONT as *const _ as u32) + 4 + char_index as u32) as *const u32);
3625                        glyph_pos = 1
3626                    }
3627                    x_pixel_counter = 7;
3628                    pos = x + (y + y_pixel_counter) * frame_width;
3629                    pos = pos * 4 + edram_address as i32 + frame_buffer as i32;
3630                    loop {
3631                        match pixel_size {
3632                            DisplayPixelFormat::Psm8888 => {
3633                                if font_glyph & glyph_pos != 0 {
3634                                    *((pos as u32 + 0x4000_0000) as *mut u32) = color;
3635                                }
3636                            }
3637                            _ => {
3638                                *((pos as u32 + 0x4000_0002) as *mut u16) = color as u16;
3639                            }
3640                        }
3641                        x_pixel_counter -= 1;
3642                        glyph_pos <<= 1;
3643                        pos += 4;
3644                        if x_pixel_counter <= -1 {
3645                            break;
3646                        }
3647                    }
3648                    y_pixel_counter += 1;
3649                    if 8 <= y_pixel_counter {
3650                        break;
3651                    }
3652                }
3653            }
3654            char_buffer_used -= 1;
3655            char_struct_ptr = ((char_struct_ptr as u32) + 16) as *mut DebugCharStruct;
3656            if char_buffer_used == 0 {
3657                break;
3658            }
3659        }
3660        CHAR_BUFFER_USED = 0;
3661    }
3662}