From f870e2d4127d3b06fe6acd19025aba9eaa254edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20Sobczy=C5=84ski?= <32263891+h4570@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:41:23 +0100 Subject: [PATCH] libpacket2 with VU support (#149) * added VU1 support! * small fixes * fixed documentation * added packet2 structs and enums * added packet2 send wrapper in libdma * fixed enums, added tte * added chain functions * added unpack modes to types * added vif related functions * removed unecessary comment * changed naming convention * changed naming convention * fixed include dependency * added packet2 * moved vu1 class from libdraw to sample * implemented packet2 in sample * refactored vu1.c to libvu * moved vif_added_bytes to packet2 * fixed flush cache bug * refactored libvu * fixed spaces * small naming fix * added channel choice in upload_program() * naming fixes + wait gif tag * small upgrade to start program * removed unecessary flush * refactored to one create() func * moved vu stuff from libvu to libpacket2 * fixed typo * -_vu_unpack_add*, refactored _vu to _helpers * comments fix * changed helpers to utils * added print(), fixed bug in add() * added offset, double buffer to open_unpack() * moved func upload_program() added "utils_" * +print_qw(), changed packet2_add() * added some warning in comment * changed offset to dest_address, fixed counting bug * refactored sample * dma wait() before send() --- ee/Makefile | 2 +- ee/dma/Makefile | 2 + ee/dma/include/dma.h | 10 + ee/dma/src/dma.c | 27 ++ ee/draw/include/draw3d.h | 22 + ee/draw/samples/vu1/Makefile | 12 + ee/draw/samples/vu1/Makefile.sample | 41 ++ ee/draw/samples/vu1/main.c | 396 ++++++++++++++++++ ee/draw/samples/vu1/mesh_data.c | 85 ++++ .../samples/vu1/micro_programs/draw_3D.vcl | 139 ++++++ .../samples/vu1/micro_programs/draw_3D.vsm | 97 +++++ ee/draw/samples/vu1/zbyszek.raw | 1 + ee/packet2/Makefile | 17 + ee/packet2/include/packet2.h | 206 +++++++++ ee/packet2/include/packet2_chain.h | 240 +++++++++++ ee/packet2/include/packet2_types.h | 350 ++++++++++++++++ ee/packet2/include/packet2_utils.h | 252 +++++++++++ ee/packet2/include/packet2_vif.h | 364 ++++++++++++++++ ee/packet2/src/erl-support.c | 18 + ee/packet2/src/packet2.c | 133 ++++++ ee/packet2/src/packet2_vif.c | 32 ++ 21 files changed, 2445 insertions(+), 1 deletion(-) create mode 100644 ee/draw/samples/vu1/Makefile create mode 100644 ee/draw/samples/vu1/Makefile.sample create mode 100644 ee/draw/samples/vu1/main.c create mode 100644 ee/draw/samples/vu1/mesh_data.c create mode 100755 ee/draw/samples/vu1/micro_programs/draw_3D.vcl create mode 100644 ee/draw/samples/vu1/micro_programs/draw_3D.vsm create mode 100644 ee/draw/samples/vu1/zbyszek.raw create mode 100644 ee/packet2/Makefile create mode 100644 ee/packet2/include/packet2.h create mode 100644 ee/packet2/include/packet2_chain.h create mode 100644 ee/packet2/include/packet2_types.h create mode 100644 ee/packet2/include/packet2_utils.h create mode 100644 ee/packet2/include/packet2_vif.h create mode 100644 ee/packet2/src/erl-support.c create mode 100644 ee/packet2/src/packet2.c create mode 100644 ee/packet2/src/packet2_vif.c diff --git a/ee/Makefile b/ee/Makefile index 03895783cf9..65e839e53cd 100644 --- a/ee/Makefile +++ b/ee/Makefile @@ -8,7 +8,7 @@ SUBDIRS = erl kernel kernel-nopatch libc rpc debug \ eedebug sbv dma graph math3d \ - packet draw erl-loader mpeg libgs \ + packet packet2 draw erl-loader mpeg libgs \ libvux font input inputx network iopreboot \ elf-loader diff --git a/ee/dma/Makefile b/ee/dma/Makefile index 3e2a78d8bcc..151d6b9bb1b 100644 --- a/ee/dma/Makefile +++ b/ee/dma/Makefile @@ -8,6 +8,8 @@ EE_OBJS = dma.o erl-support.o +EE_INCS := $(EE_INCS) -I$(PS2SDKSRC)/ee/packet2/include + include $(PS2SDKSRC)/Defs.make include $(PS2SDKSRC)/ee/Rules.lib.make include $(PS2SDKSRC)/ee/Rules.make diff --git a/ee/dma/include/dma.h b/ee/dma/include/dma.h index 5774edb11da..0543d707087 100644 --- a/ee/dma/include/dma.h +++ b/ee/dma/include/dma.h @@ -17,6 +17,7 @@ #define __DMA_H__ #include +#include #define DMA_CHANNEL_VIF0 0x00 #define DMA_CHANNEL_VIF1 0x01 @@ -50,6 +51,15 @@ void dma_wait_fast(void); /** Wait until the specified dma channel is ready. */ int dma_channel_wait(int channel, int timeout); +/** + * Send packet2. + * Type chain/normal is choosen from packet2_t. + * @param packet2 Pointer to packet. + * @param channel DMA channel. + * @param flush_cache Should be cache flushed before send? + */ +void dma_channel_send_packet2(packet2_t *packet2, int channel, u8 flush_cache); + /** Send a dmachain to the specified dma channel. */ int dma_channel_send_chain(int channel, void *data, int qwc, int flags, int spr); diff --git a/ee/dma/src/dma.c b/ee/dma/src/dma.c index 29ae8ee82c7..73860c524d9 100644 --- a/ee/dma/src/dma.c +++ b/ee/dma/src/dma.c @@ -154,6 +154,33 @@ int dma_channel_wait(int channel, int timeout) } +void dma_channel_send_packet2(packet2_t *packet2, int channel, u8 flush_cache) +{ + // dma_channel_send_chain does NOT flush all data that is "source chained" + if (packet2->mode == P2_MODE_CHAIN) + { + // "dma_channel_send_normal always flushes the data cache" + if (flush_cache) + FlushCache(0); + dma_channel_send_chain( + channel, + (void *)((u32)packet2->base & 0x0FFFFFFF), + 0, + packet2->tte ? DMA_FLAG_TRANSFERTAG : 0, + 0); + } + else + { + dma_channel_send_normal( + channel, + (void *)((u32)packet2->base & 0x0FFFFFFF), // make ptr normal + ((u32)packet2->next - (u32)packet2->base) >> 4, // length in qwords + 0, + 0); + } +} + + int dma_channel_send_chain(int channel, void *data, int data_size, int flags, int spr) { diff --git a/ee/draw/include/draw3d.h b/ee/draw/include/draw3d.h index 72c9e21c35e..091763b7b37 100644 --- a/ee/draw/include/draw3d.h +++ b/ee/draw/include/draw3d.h @@ -35,6 +35,28 @@ ((u64)GIF_REG_ST) << 4 | \ ((u64)GIF_REG_XYZ2) << 8 +/** + * Sandro: + * Similar to DRAW_STQ_REGLIST, but order of ST and RGBAQ is swapped. + * Needed for REGLIST mode which is used mostly in VU1, because of nature of 128bit registers. + * Without that, texture perspective correction will not work. + * Bad example: + * 1. RGBA -> RGBAQ (q was not set!) + * 2. STQ -> ST + * 3. XYZ2 + * Good example: + * 1. STQ -> ST + * 2. RGBA -> RGBAQ (q grabbed from STQ) + * 3. XYZ2 + * For more details, please check: + * EE_Overview_Manual.pdf - 3.3 Data transfer to GS + * GS_Users_Manual.pdf - 3.4.10 Perspective correction + */ +#define DRAW_STQ2_REGLIST \ + ((u64)GIF_REG_ST) << 0 | \ + ((u64)GIF_REG_RGBAQ) << 4 | \ + ((u64)GIF_REG_XYZ2) << 8 + #ifdef __cplusplus extern "C" { #endif diff --git a/ee/draw/samples/vu1/Makefile b/ee/draw/samples/vu1/Makefile new file mode 100644 index 00000000000..f61f2638cb7 --- /dev/null +++ b/ee/draw/samples/vu1/Makefile @@ -0,0 +1,12 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +SAMPLE_DIR = draw/vu1 + +include $(PS2SDKSRC)/Defs.make +include $(PS2SDKSRC)/samples/Rules.samples diff --git a/ee/draw/samples/vu1/Makefile.sample b/ee/draw/samples/vu1/Makefile.sample new file mode 100644 index 00000000000..44abe3a03dc --- /dev/null +++ b/ee/draw/samples/vu1/Makefile.sample @@ -0,0 +1,41 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +EE_BIN = vu1.elf +EE_OBJS = micro_programs/draw_3D.o \ + main.o +EE_LIBS = -ldraw -lgraph -lmath3d -lpacket2 -ldma +EE_DVP = dvp-as +#EE_VCL = vcl + +all: zbyszek.c $(EE_BIN) + $(EE_STRIP) --strip-all $(EE_BIN) + +# Original VCL tool preferred. +# It can be runned on WSL, but with some tricky commands: +# https://github.com/microsoft/wsl/issues/2468#issuecomment-374904520 +#%.vsm: %.vcl +# $(EE_VCL) $< >> $@ + +%.o: %.vsm + $(EE_DVP) $< -o $@ + +zbyszek.c: + bin2c zbyszek.raw zbyszek.c zbyszek + +clean: + rm -f $(EE_BIN) $(EE_OBJS) zbyszek.c + +run: $(EE_BIN) + ps2client execee host:$(EE_BIN) + +reset: + ps2client reset + +include $(PS2SDK)/samples/Makefile.pref +include $(PS2SDK)/samples/Makefile.eeglobal diff --git a/ee/draw/samples/vu1/main.c b/ee/draw/samples/vu1/main.c new file mode 100644 index 00000000000..cc6f737f8be --- /dev/null +++ b/ee/draw/samples/vu1/main.c @@ -0,0 +1,396 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# VU1 and libpacket2 showcase. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zbyszek.c" +#include "mesh_data.c" + +// --- +// Variables declared as global for tutorial only! +// --- + +/** Data of our texture (24bit, RGB8) */ +extern unsigned char zbyszek[]; + +/** + * Data of VU1 micro program (draw_3D.vcl/vsm). + * How we can use it: + * 1. Upload program to VU1. + * 2. Send calculated local_screen matrix once per mesh (3D object) + * 3. Set buffers size. (double-buffering described below) + * 4. Send packet with: lod, clut, tex buffer, scale vector, rgba, verts and sts. + * What this program is doing? + * 1. Load local_screen. + * 2. Zero clipping flag. + * 3. Set current buffer start address from TOP register (xtop command) + * To use pararelism, we set two buffers in the VU1. It means, that when + * VU1 is working with one verts packet, we can load second one into another buffer. + * xtop command is automatically switching buffers. I think that AAA games used + * quad buffers (TOP+TOPS) which can give best performance and no VIF_FLUSH should be needed. + * 4. Load rest of data. + * 5. Prepare GIF tag. + * 6. For every vertex: transform, clip, scale, perspective divide. + * 7. Send it to GS via XGKICK command. + */ +extern u32 VU1Draw3D_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1Draw3D_CodeEnd __attribute__((section(".vudata"))); + +VECTOR object_rotation = {0.00f, 0.00f, 0.00f, 1.00f}; +VECTOR camera_position = {140.00f, 140.00f, 40.00f, 1.00f}; +VECTOR camera_rotation = {0.00f, 0.00f, 0.00f, 1.00f}; +MATRIX local_world, world_view, view_screen, local_screen; + +/** + * Packets for sending VU data + * Each packet will have: + * a) View/Projection matrix (calculated every frame) + * b) Cube data (prim,lod,vertices,sts,...) added from zbyszek_packet. + */ +packet2_t *vif_packets[2] __attribute__((aligned(64))); +packet2_t *curr_vif_packet; + +/** Cube data */ +packet2_t *zbyszek_packet; + +u8 context = 0; + +/** Set GS primitive type of drawing. */ +prim_t prim; + +/** + * Color look up table. + * Needed for texture. + */ +clutbuffer_t clut; + +/** + * Level of details. + * Needed for texture. + */ +lod_t lod; + +/** + * Helper arrays. + * Needed for calculations. + */ +VECTOR *c_verts __attribute__((aligned(128))), *c_sts __attribute__((aligned(128))); + +/** Calculate packet for cube data */ +void calculate_cube(texbuffer_t *t_texbuff) +{ + packet2_utils_vu_open_unpack(zbyszek_packet, 0, 1); + packet2_add_float(zbyszek_packet, 2048.0F); // scale + packet2_add_float(zbyszek_packet, 2048.0F); // scale + packet2_add_float(zbyszek_packet, ((float)0xFFFFFF) / 32.0F); // scale + packet2_add_s32(zbyszek_packet, faces_count); // vertex count + packet2_utils_gif_add_set(zbyszek_packet, 1); + packet2_utils_gs_add_lod(zbyszek_packet, &lod); + packet2_utils_gs_add_texbuff_clut(zbyszek_packet, t_texbuff, &clut); + packet2_utils_gs_add_prim_giftag(zbyszek_packet, &prim, faces_count, DRAW_STQ2_REGLIST, 3, 0); + u8 j = 0; // RGBA + for (j = 0; j < 4; j++) + packet2_add_u32(zbyszek_packet, 128); + packet2_utils_vu_close_unpack(zbyszek_packet); +} + +/** Calculate cube position and add packet with cube data */ +void draw_cube(VECTOR t_object_position, texbuffer_t *t_texbuff) +{ + create_local_world(local_world, t_object_position, object_rotation); + create_world_view(world_view, camera_position, camera_rotation); + create_local_screen(local_screen, local_world, world_view, view_screen); + curr_vif_packet = vif_packets[context]; + packet2_reset(curr_vif_packet, 0); + + // Upload matrix + // packet2_utils_vu_add_unpack_data() is automatically increasing packet vif_added_bytes + packet2_utils_vu_add_unpack_data(curr_vif_packet, 0, &local_screen, 8, 0); + + // Reset, because now, we will use double buffer + // We don't wan't to unpack at 8 + beggining of buffer, but at + // the beggining of buffer + curr_vif_packet->vif_added_bytes = 0; + + // Merge packets (memcpy()) + packet2_add(curr_vif_packet, zbyszek_packet); + + // If you don't want to use packet2_add() and create reference: + // 1. Remove packet2_add(); + // 2. Add packet2_utils_vu_add_unpack_data(curr_vif_packet, packet2_get_vif_added_qws(curr_vif_packet), zbyszek_packet->base, packet2_get_vif_added_qws(zbyszek_packet), 1); + // 3. In calculate_cube() remove open/close unpack. + + packet2_utils_vu_add_unpack_data(curr_vif_packet, packet2_get_vif_added_qws(curr_vif_packet), c_verts, faces_count, 1); + packet2_utils_vu_add_unpack_data(curr_vif_packet, packet2_get_vif_added_qws(curr_vif_packet), c_sts, faces_count, 1); + packet2_utils_vu_add_start_program(curr_vif_packet, 0); + + packet2_utils_vu_add_end_tag(curr_vif_packet); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(curr_vif_packet, DMA_CHANNEL_VIF1, 1); + + // Switch packet, so we can proceed during DMA transfer + context = !context; +} + +/** Some initialization of GS and VRAM allocation */ +void init_gs(framebuffer_t *t_frame, zbuffer_t *t_z, texbuffer_t *t_texbuff) +{ + // Define a 32-bit 640x512 framebuffer. + t_frame->width = 640; + t_frame->height = 512; + t_frame->mask = 0; + t_frame->psm = GS_PSM_32; + t_frame->address = graph_vram_allocate(t_frame->width, t_frame->height, t_frame->psm, GRAPH_ALIGN_PAGE); + + // Enable the zbuffer. + t_z->enable = DRAW_ENABLE; + t_z->mask = 0; + t_z->method = ZTEST_METHOD_GREATER_EQUAL; + t_z->zsm = GS_ZBUF_32; + t_z->address = graph_vram_allocate(t_frame->width, t_frame->height, t_z->zsm, GRAPH_ALIGN_PAGE); + + // Allocate some vram for the texture buffer + t_texbuff->width = 128; + t_texbuff->psm = GS_PSM_24; + t_texbuff->address = graph_vram_allocate(128, 128, GS_PSM_24, GRAPH_ALIGN_BLOCK); + + // Initialize the screen and tie the first framebuffer to the read circuits. + graph_initialize(t_frame->address, t_frame->width, t_frame->height, t_frame->psm, 0, 0); +} + +/** Some initialization of GS 2 */ +void init_drawing_environment(framebuffer_t *t_frame, zbuffer_t *t_z) +{ + packet2_t *packet2 = packet2_create(20, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0); + + // This will setup a default drawing environment. + packet2_update(packet2, draw_setup_environment(packet2->next, 0, t_frame, t_z)); + + // Now reset the primitive origin to 2048-width/2,2048-height/2. + packet2_update(packet2, draw_primitive_xyoffset(packet2->next, 0, (2048 - 320), (2048 - 256))); + + // Finish setting up the environment. + packet2_update(packet2, draw_finish(packet2->next)); + + // Now send the packet, no need to wait since it's the first. + dma_channel_send_packet2(packet2, DMA_CHANNEL_GIF, 1); + dma_wait_fast(); + + packet2_free(packet2); +} + +/** Send texture data to GS. */ +void send_texture(texbuffer_t *texbuf) +{ + packet2_t *packet2 = packet2_create(50, P2_TYPE_NORMAL, P2_MODE_CHAIN, 0); + packet2_update(packet2, draw_texture_transfer(packet2->next, zbyszek, 128, 128, GS_PSM_24, texbuf->address, texbuf->width)); + packet2_update(packet2, draw_texture_flush(packet2->next)); + dma_channel_send_packet2(packet2, DMA_CHANNEL_GIF, 1); + dma_wait_fast(); + packet2_free(packet2); +} + +/** Send packet which will clear our screen. */ +void clear_screen(framebuffer_t *frame, zbuffer_t *z) +{ + packet2_t *clear = packet2_create(35, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0); + + // Clear framebuffer but don't update zbuffer. + packet2_update(clear, draw_disable_tests(clear->next, 0, z)); + packet2_update(clear, draw_clear(clear->next, 0, 2048.0f - 320.0f, 2048.0f - 256.0f, frame->width, frame->height, 0x40, 0x40, 0x40)); + packet2_update(clear, draw_enable_tests(clear->next, 0, z)); + packet2_update(clear, draw_finish(clear->next)); + + // Now send our current dma chain. + dma_wait_fast(); + dma_channel_send_packet2(clear, DMA_CHANNEL_GIF, 1); + + packet2_free(clear); + + // Wait for scene to finish drawing + draw_wait_finish(); +} + +void set_lod_clut_prim_tex_buff(texbuffer_t *t_texbuff) +{ + lod.calculation = LOD_USE_K; + lod.max_level = 0; + lod.mag_filter = LOD_MAG_NEAREST; + lod.min_filter = LOD_MIN_NEAREST; + lod.l = 0; + lod.k = 0; + + clut.storage_mode = CLUT_STORAGE_MODE1; + clut.start = 0; + clut.psm = 0; + clut.load_method = CLUT_NO_LOAD; + clut.address = 0; + + // Define the triangle primitive we want to use. + prim.type = PRIM_TRIANGLE; + prim.shading = PRIM_SHADE_GOURAUD; + prim.mapping = DRAW_ENABLE; + prim.fogging = DRAW_DISABLE; + prim.blending = DRAW_ENABLE; + prim.antialiasing = DRAW_DISABLE; + prim.mapping_type = PRIM_MAP_ST; + prim.colorfix = PRIM_UNFIXED; + + t_texbuff->info.width = draw_log2(128); + t_texbuff->info.height = draw_log2(128); + t_texbuff->info.components = TEXTURE_COMPONENTS_RGB; + t_texbuff->info.function = TEXTURE_FUNCTION_DECAL; +} + +void render(framebuffer_t *t_frame, zbuffer_t *t_z, texbuffer_t *t_texbuff) +{ + int i, j; + + set_lod_clut_prim_tex_buff(t_texbuff); + + /** + * Allocate some space for object position calculating. + * c_ prefix = calc_ + */ + c_verts = (VECTOR *)memalign(128, sizeof(VECTOR) * faces_count); + c_sts = (VECTOR *)memalign(128, sizeof(VECTOR) * faces_count); + + VECTOR c_zbyszek_position; + + for (i = 0; i < faces_count; i++) + { + c_verts[i][0] = vertices[faces[i]][0]; + c_verts[i][1] = vertices[faces[i]][1]; + c_verts[i][2] = vertices[faces[i]][2]; + c_verts[i][3] = vertices[faces[i]][3]; + + c_sts[i][0] = sts[faces[i]][0]; + c_sts[i][1] = sts[faces[i]][1]; + c_sts[i][2] = sts[faces[i]][2]; + c_sts[i][3] = sts[faces[i]][3]; + } + + // Create the view_screen matrix. + create_view_screen(view_screen, graph_aspect_ratio(), -3.00f, 3.00f, -3.00f, 3.00f, 1.00f, 2000.00f); + calculate_cube(t_texbuff); + + // The main loop... + for (;;) + { + // Spin the cube a bit. + object_rotation[0] += 0.008f; + while (object_rotation[0] > 3.14f) + { + object_rotation[0] -= 6.28f; + } + object_rotation[1] += 0.012f; + while (object_rotation[1] > 3.14f) + { + object_rotation[1] -= 6.28f; + } + + camera_position[2] += .5F; + camera_rotation[2] += 0.002f; + if (camera_position[2] >= 400.0F) + { + camera_position[2] = 40.0F; + camera_rotation[2] = 0.00f; + } + + clear_screen(t_frame, t_z); + + for (i = 0; i < 8; i++) + { + c_zbyszek_position[0] = i * 40.0F; + for (j = 0; j < 8; j++) + { + c_zbyszek_position[1] = j * 40.0F; + draw_cube(c_zbyszek_position, t_texbuff); + } + } + + graph_wait_vsync(); + } +} + +void vu1_set_double_buffer_settings() +{ + packet2_t *packet2 = packet2_create(1, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1); + packet2_utils_vu_add_double_buffer(packet2, 8, 496); + packet2_utils_vu_add_end_tag(packet2); + dma_channel_send_packet2(packet2, DMA_CHANNEL_VIF1, 1); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + packet2_free(packet2); +} + +void vu1_upload_micro_program() +{ + u32 packet_size = + packet2_utils_get_packet_size_for_program(&VU1Draw3D_CodeStart, &VU1Draw3D_CodeEnd) + 1; // + 1 for end tag + packet2_t *packet2 = packet2_create(packet_size, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1); + packet2_vif_add_micro_program(packet2, 0, &VU1Draw3D_CodeStart, &VU1Draw3D_CodeEnd); + packet2_utils_vu_add_end_tag(packet2); + dma_channel_send_packet2(packet2, DMA_CHANNEL_VIF1, 1); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + packet2_free(packet2); +} + +int main(int argc, char **argv) +{ + + // Init DMA channels. + dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0); + dma_channel_initialize(DMA_CHANNEL_VIF1, NULL, 0); + dma_channel_fast_waits(DMA_CHANNEL_GIF); + dma_channel_fast_waits(DMA_CHANNEL_VIF1); + + // Initialize vif packets + zbyszek_packet = packet2_create(10, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1); + vif_packets[0] = packet2_create(11, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1); + vif_packets[1] = packet2_create(11, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1); + + vu1_upload_micro_program(); + vu1_set_double_buffer_settings(); + + // The buffers to be used. + framebuffer_t frame; + zbuffer_t z; + texbuffer_t texbuff; + + // Init the GS, framebuffer, zbuffer, and texture buffer. + init_gs(&frame, &z, &texbuff); + + // Init the drawing environment and framebuffer. + init_drawing_environment(&frame, &z); + + // Load the texture into vram. + send_texture(&texbuff); + + // Render textured cube + render(&frame, &z, &texbuff); + + packet2_free(vif_packets[0]); + packet2_free(vif_packets[1]); + packet2_free(zbyszek_packet); + + // Sleep + SleepThread(); + + // End program. + return 0; +} diff --git a/ee/draw/samples/vu1/mesh_data.c b/ee/draw/samples/vu1/mesh_data.c new file mode 100644 index 00000000000..5e11fec62aa --- /dev/null +++ b/ee/draw/samples/vu1/mesh_data.c @@ -0,0 +1,85 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +*/ + +int faces_count = 36; + +/** + * Array of vertex indexes. + * 3 faces = 1 triangle + */ +int faces[36] = { + 0, 1, 2, + 1, 2, 3, + 4, 5, 6, + 5, 6, 7, + 8, 9, 10, + 9, 10, 11, + 12, 13, 14, + 13, 14, 15, + 16, 17, 18, + 17, 18, 19, + 20, 21, 22, + 21, 22, 23}; + +int vertex_count = 24; + +VECTOR vertices[24] = { + {10.00f, 10.00f, 10.00f, 1.00f}, + {10.00f, 10.00f, -10.00f, 1.00f}, + {10.00f, -10.00f, 10.00f, 1.00f}, + {10.00f, -10.00f, -10.00f, 1.00f}, + {-10.00f, 10.00f, 10.00f, 1.00f}, + {-10.00f, 10.00f, -10.00f, 1.00f}, + {-10.00f, -10.00f, 10.00f, 1.00f}, + {-10.00f, -10.00f, -10.00f, 1.00f}, + {-10.00f, 10.00f, 10.00f, 1.00f}, + {10.00f, 10.00f, 10.00f, 1.00f}, + {-10.00f, 10.00f, -10.00f, 1.00f}, + {10.00f, 10.00f, -10.00f, 1.00f}, + {-10.00f, -10.00f, 10.00f, 1.00f}, + {10.00f, -10.00f, 10.00f, 1.00f}, + {-10.00f, -10.00f, -10.00f, 1.00f}, + {10.00f, -10.00f, -10.00f, 1.00f}, + {-10.00f, 10.00f, 10.00f, 1.00f}, + {10.00f, 10.00f, 10.00f, 1.00f}, + {-10.00f, -10.00f, 10.00f, 1.00f}, + {10.00f, -10.00f, 10.00f, 1.00f}, + {-10.00f, 10.00f, -10.00f, 1.00f}, + {10.00f, 10.00f, -10.00f, 1.00f}, + {-10.00f, -10.00f, -10.00f, 1.00f}, + {10.00f, -10.00f, -10.00f, 1.00f}}; + +/** Texture coordinates */ +VECTOR sts[24] = { + {0.00f, 0.00f, 1.00f, 0.00f}, + {1.00f, 0.00f, 1.00f, 0.00f}, + {0.00f, 1.00f, 1.00f, 0.00f}, + {1.00f, 1.00f, 1.00f, 0.00f}, + {0.00f, 0.00f, 1.00f, 0.00f}, + {1.00f, 0.00f, 1.00f, 0.00f}, + {0.00f, 1.00f, 1.00f, 0.00f}, + {1.00f, 1.00f, 1.00f, 0.00f}, + {0.00f, 0.00f, 1.00f, 0.00f}, + {1.00f, 0.00f, 1.00f, 0.00f}, + {0.00f, 1.00f, 1.00f, 0.00f}, + {1.00f, 1.00f, 1.00f, 0.00f}, + {0.00f, 0.00f, 1.00f, 0.00f}, + {1.00f, 0.00f, 1.00f, 0.00f}, + {0.00f, 1.00f, 1.00f, 0.00f}, + {1.00f, 1.00f, 1.00f, 0.00f}, + {0.00f, 0.00f, 1.00f, 0.00f}, + {1.00f, 0.00f, 1.00f, 0.00f}, + {0.00f, 1.00f, 1.00f, 0.00f}, + {1.00f, 1.00f, 1.00f, 0.00f}, + {0.00f, 0.00f, 1.00f, 0.00f}, + {1.00f, 0.00f, 1.00f, 0.00f}, + {0.00f, 1.00f, 1.00f, 0.00f}, + {1.00f, 1.00f, 1.00f, 0.00f}}; diff --git a/ee/draw/samples/vu1/micro_programs/draw_3D.vcl b/ee/draw/samples/vu1/micro_programs/draw_3D.vcl new file mode 100755 index 00000000000..15c46f5881a --- /dev/null +++ b/ee/draw/samples/vu1/micro_programs/draw_3D.vcl @@ -0,0 +1,139 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro Sobczyński +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D.vcl | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + +.syntax new +.name VU1Draw3D +.vu +.init_vf_all +.init_vi_all + +--enter +--endenter + + ;//////////// --- Load data 1 --- ///////////// + ; Updated once per mesh + lq matrixRow0, 0(vi00) ; load view-projection matrix + lq matrixRow1, 1(vi00) + lq matrixRow2, 2(vi00) + lq matrixRow3, 3(vi00) + ;///////////////////////////////////////////// + + fcset 0x000000 ; VCL won't let us use CLIP without first zeroing + ; the clip flags + + ;//////////// --- Load data 2 --- ///////////// + ; Updated dynamically + xtop iBase + + lq.xyz scale, 0(iBase) ; load program params + ; float : X, Y, Z - scale vector that we will use to scale the verts after projecting them. + ; float : W - vert count. + lq gifSetTag, 1(iBase) ; GIF tag - set + lq texGifTag1, 2(iBase) ; GIF tag - texture LOD + lq texGifTag2, 3(iBase) ; GIF tag - texture buffer & CLUT + lq primTag, 4(iBase) ; GIF tag - tell GS how many data we will send + lq rgba, 5(iBase) ; RGBA + ; u32 : R, G, B, A (0-128) + iaddiu vertexData, iBase, 6 ; pointer to vertex data + ilw.w vertCount, 0(iBase) ; load vert count from scale vector + iadd stqData, vertexData, vertCount ; pointer to stq + iadd kickAddress, stqData, vertCount ; pointer for XGKICK + iadd destAddress, stqData, vertCount ; helper pointer for data inserting + ;//////////////////////////////////////////// + + ;/////////// --- Store tags --- ///////////// + sqi gifSetTag, (destAddress++) ; + sqi texGifTag1, (destAddress++) ; texture LOD tag + sqi gifSetTag, (destAddress++) ; + sqi texGifTag2, (destAddress++) ; texture buffer & CLUT tag + sqi primTag, (destAddress++) ; prim + tell gs how many data will be + ;//////////////////////////////////////////// + + ;/////////////// --- Loop --- /////////////// + iadd vertexCounter, iBase, vertCount ; loop vertCount times + vertexLoop: + + ;////////// --- Load loop data --- ////////// + lq vertex, 0(vertexData) ; load xyz + ; float : X, Y, Z + ; any32 : _ = 0 + lq stq, 0(stqData) ; load stq + ; float : S, T + ; any32 : Q = 1 ; 1, because we will mul this by 1/vert[w] and this + ; will be our q for texture perspective correction + ; any32 : _ = 0 + ;//////////////////////////////////////////// + + + ;////////////// --- Vertex --- ////////////// + mul acc, matrixRow0, vertex[x] ; transform each vertex by the matrix + madd acc, matrixRow1, vertex[y] + madd acc, matrixRow2, vertex[z] + madd vertex, matrixRow3, vertex[w] + + clipw.xyz vertex, vertex ; Dr. Fortuna: This instruction checks if the vertex is outside + ; the viewing frustum. If it is, then the appropriate + ; clipping flags are set + fcand VI01, 0x3FFFF ; Bitwise AND the clipping flags with 0x3FFFF, this makes + ; sure that we get the clipping judgement for the last three + ; verts (i.e. that make up the triangle we are about to draw) + iaddiu iADC, VI01, 0x7FFF ; Add 0x7FFF. If any of the clipping flags were set this will + ; cause the triangle not to be drawn (any values above 0x8000 + ; that are stored in the w component of XYZ2 will set the ADC + ; bit, which tells the GS not to perform a drawing kick on this + ; triangle. + + isw.w iADC, 2(destAddress) + + div q, vf00[w], vertex[w] ; perspective divide (1/vert[w]): + mul.xyz vertex, vertex, q + mula.xyz acc, scale, vf00[w] ; scale to GS screen space + madd.xyz vertex, vertex, scale ; multiply and add the scales -> vert = vert * scale + scale + ftoi4.xyz vertex, vertex ; convert vertex to 12:4 fixed point format + ;//////////////////////////////////////////// + + + ;//////////////// --- ST --- //////////////// + mulq modStq, stq, q + ;//////////////////////////////////////////// + + + ;//////////// --- Store data --- //////////// + sq modStq, 0(destAddress) ; STQ + sq rgba, 1(destAddress) ; RGBA ; q is grabbed from stq + sq.xyz vertex, 2(destAddress) ; XYZ2 + ;//////////////////////////////////////////// + + iaddiu vertexData, vertexData, 1 + iaddiu stqData, stqData, 1 + iaddiu destAddress, destAddress, 3 + + iaddi vertexCounter, vertexCounter, -1 ; decrement the loop counter + ibne vertexCounter, iBase, vertexLoop ; and repeat if needed + + ;//////////////////////////////////////////// + + --barrier + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--exit +--endexit diff --git a/ee/draw/samples/vu1/micro_programs/draw_3D.vsm b/ee/draw/samples/vu1/micro_programs/draw_3D.vsm new file mode 100644 index 00000000000..831dcd9a3e3 --- /dev/null +++ b/ee/draw/samples/vu1/micro_programs/draw_3D.vsm @@ -0,0 +1,97 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro Sobczyński +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D.vsm | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + + +;------------------------- +;------------------------- +;-----VCL CODE------------ +;------------------------- +;------------------------- +; ================================================= +; flowMon::Emit() vcl 1.4beta7 produced this code: + .vu + .align 4 + .global VU1Draw3D_CodeStart + .global VU1Draw3D_CodeEnd +VU1Draw3D_CodeStart: +__v_draw_3D_vcl_4: +; _LNOPT_w=[ normal2 ] 23 [23 0] 23 [__v_draw_3D_vcl_4] + NOP lq VF01,0(VI00) + NOP xtop VI02 + NOP lq VF02,1(VI00) + NOP lq VF06,1(VI02) + NOP lq VF09,2(VI02) + NOP lq VF08,3(VI02) + NOP lq VF07,4(VI02) + NOP lq VF03,2(VI00) + NOP iaddiu VI03,VI02,0x00000006 + NOP ilw.w VI07,0(VI02) + NOP lq VF04,3(VI00) + NOP fcset 0 + NOP lq.xyz VF05,0(VI02) + NOP iadd VI04,VI03,VI07 + NOP iadd VI06,VI04,VI07 + NOP sqi VF06,(VI06++) + NOP sqi VF09,(VI06++) + NOP sqi VF06,(VI06++) + NOP sqi VF08,(VI06++) + NOP lq VF06,5(VI02) + NOP iadd VI05,VI04,VI07 + NOP sqi VF07,(VI06++) + NOP iadd VI07,VI02,VI07 +vertexLoop: +; _LNOPT_w=[ normal2 ] 21 [31 14] 31 [vertexLoop] + NOP lq VF07,0(VI03) + mulax ACC,VF01,VF07x sq VF06,1(VI06) ; STALL_LATENCY ?3 + madday ACC,VF02,VF07y lq VF08,0(VI04) + maddaz ACC,VF03,VF07z iaddiu VI06,VI06,0x00000003 + maddw VF07,VF04,VF07w NOP + clipw.xyz VF07xyz,VF07w div Q,VF00w,VF07w ; STALL_LATENCY ?3 + NOP NOP + NOP NOP + NOP NOP + NOP NOP + NOP NOP + NOP NOP + mulq.xyz VF07,VF07,Q fcand VI01,262143 + mulaw.xyz ACC,VF05,VF00w iaddiu VI03,VI03,0x00000001 + madd.xyz VF07,VF07,VF05 iaddiu VI04,VI04,0x00000001 ; STALL_LATENCY ?2 + mulq VF08,VF08,Q isubiu VI07,VI07,1 + ftoi4.xyz VF07,VF07 iaddiu VI01,VI01,0x00007fff ; STALL_LATENCY ?2 + NOP isw.w VI01,-1(VI06) + NOP sq VF08,-3(VI06) + NOP ibne VI07,VI02,vertexLoop + NOP sq.xyz VF07,-1(VI06) +; _LNOPT_w=[ normal2 ] 3 [1 0] 3 [__v_draw_3D_vcl_7] + NOP xgkick VI05 + NOP[E] NOP + NOP NOP + .align 4 +VU1Draw3D_CodeEnd: +; iCount=47 +; register stats: +; 8 VU User integer +; 10 VU User floating point +;------------------------- +;------------------------- +;------------------------- +;------------------------- +;------------------------- diff --git a/ee/draw/samples/vu1/zbyszek.raw b/ee/draw/samples/vu1/zbyszek.raw new file mode 100644 index 00000000000..f2f99a0618f --- /dev/null +++ b/ee/draw/samples/vu1/zbyszek.raw @@ -0,0 +1 @@ +}Ƕν±ʹvmrioinklkjkfi`b}[^xTWqSVpRUqQUrSWx[^hlqvvzv{uzmrbg[_X\V[V\V\V\~W\W]X^Z`Z_W\TYRW~OT{NS{SXUZW\X]W\MRxBHl>Bf?@c>=^>=\<4,<0(6,%4*&4+%3+$2+$2+$2,#/*!-)"-)"-)".)".)"/'!.'!.'!.' .&-%,%,$+#*#*$+%-%.&/,%73,?:2GB:OE=SH@WKBZNE]QIbULfYPj]TnaYre\ve\ve\xe\xd[w`VsYOnVMkUMkTMkSLjRKiSLjUNlVOmUPmRLiE>[3,H}wwy~vkohjdda__}Z[xSVpMPjMOjLNiNQkVXuY]{\`_bimottxuztykp`eZ_Y]V[V\W]X]Z_\b^c`e_d]b[`W\UZUZUZW\X]Z_X]SXINsCHkADg?@b?@`?>\<=6IC;PG?VJBYJBYKCZLD\OF_QHbTLeVMg[Rl_VpaYsd[ud[wd[wc[waXu]SrYOnTMkTMkTMkTMkVNlXQoZSqZSqXQoNGe=6Sʶ|yy{}qsimhhdeba`~]]zXZuUXrTWqZ\v^`{bd^bX\}^aimmrqvsxrwkp`eZ_Z_X]Y^Y^Z_]b_dbgbgbg`e]b[`Z_Y^X]Z_Z_Z_Y^V\SX}OTwMQtMPsNPqLMlLKiHFa:9P30D0-@1,?3.>2-<4-<7.<4+8.(1-'2-'2-'2+$0*!-*,)+*,)+(+','!,'!,("-( .&-&.& ,'!-' .'!.)#0,%50)970B<5H@8NB:QF=UG?VI@YJB[ME^PGaTKeVMgXPi]Tn_XqaZuaZvbZwa[u`Zt_Ws^WrZSpUNkSLkTMjWPkZSp[Tr\UsZSqVOmHA_§êͺznrioimikjhhde_a|_a|dghkgibeZ^|TXy\_hkkpotqvqvkpbg]b\a[`[`[`]b_dbgdidichaf^c]b[`[`Z_Z_X]V[UZTYTY~TY}TZ|TZ|UYzWXxXWuVToHG_:8L-*;,(7-(81*94+96,93*50)0/)/0)16/62+3.%-+ *)(('('')'+'!,'!,("-(-'!.(!/(".)"0("/)#0+$2+$4.'73,>7/C:2I>5MA8OB:QG>WI@ZKB\OF`SJdULfWNh[Sl]Vo`YtbZvbZwa[u`Zt`Yu`Yu]Xt[TqWOnUNkVOlYRo[Tr\Vq\VqYRoRKhvϼīĮƹwtktlrnooiibdac~hjmpjmeg_aVZzRUxY\fjjomrotpumrfkbg`e^c]b]b^cafchejejchbg`e^c]b\a[`Z_W\UZSXRW~SXSX~SX|SY{TXyVWwVVsTRmKJb@>R30A1-<0)81)65*65*53)01'-1&,3(.H=BODIF:AC6>D7?D7?E6@A5?8/:0'3,"/*!-*!-)".)"0*#2*#2)#0)#0*#1*"3+$4/'92*>5-C80G;3H?7NE=TIAXLC\NE_PHaSKdVMgYPj]Tn`WsbYvcYvbZt`Zs`Xs_Xs_Xs^WrZSqXQoUNmVOmYSo[Up\Vp[UpWPm}apñ«ïú|xoumqmhi^`|^`{giqtrtikbd[]{TWxQTwX[dhinlrotpunsjogldibg`e`eafchfjglfldjchaf_d]b\a[`Y^X]V[TYTYTYTYRY|RY{TXyUXwRSqKKfDB[?EK@J@5@2'3-",+"+-".,#1,$3,$3+#2*#1*#1)"2*"2*#5.&92*>5-B80E<4JA9PG?WJBZMD]OF_QHbTKeWOh\Sm_VqaXsbXubYs`Ys`Xt_Xs_Ws_Wr\UrZSpWOmSKiSLgZTn\Vo^Wq[Tpz\p˻ʽɿȻxwnsklh`a|_b{giqtrunoee~__zVXvQRtORuWZ}bghnlrnsotmrkpinhmej`f`fagdihlhngndlcibgaf^c]b\a[`Y^X]W\V[UZU[R[~QY{RXxRVvMPlFFa>>V97J74C31=1-80)2/&-/'*8-/>13=019+,6()5')5%)1"&/$0!%2$(6).D6@I1&/.#,/#..$0.%1-$2,#2*$2*#1("/)!1("2+#5.&90)=2-A81E>6LE=S74G4/?1+92*52(02(-6//?43F65J87I76E43@/.=+-<),:'*6#'3!&1!'4%-=.7<-75&.1"+0#,0#./$0-$0,$1+$1*#1*!/)!0(!0*"2,#4.%7/(;5-A;3IA9PF>UJAZLC\NE_QHbULfXOi\Sm^Vo`XqbYraXr`Ws_Vr_Vr_Vr^Vq[SoZRnRIeLD`LEaUMh]Tp_Vrбåξ¦ƺul~nge`~b^~efoqxyvynqgi^^uXXqIKgEGdIMmUYzafglkqlrmrmrlqlqlpjqdmbjcjelhoipiphofmdichaf`eaf`e^c]b]b\a[`Z_V[SW}RTwLNnDHd>@X;:O84F8/A7.=7-9;.6@27E57K:8Q<:S<;T<6LC;SH?YJB[MD^OG`RJcVMgZQk]Tn`WqbYraXr`Ws_Vr_Vr_Vr^Uq\So\SoQHdKB^MC`LC_PGcYPlƻi`rd]wd_|mkvw{|||xzqunpddyXWnHHbCD`JLlUXx^cekjplqlqlqmrmrmqlrgpdlcjdkgnjqjqiphofkdibgbgafaf`e^e]c]b]`[^VZSV{QRuKKjDD]?=S=8L=5F<4@=4?D:BJ=DP>AWBC\DB\EB]DA\CAYCAV@?V>=S<;P:;L68G25B/3=+0;(/8%,6$+4"(1 &0 (/!).!*-"+-",,".,".+"0*!/* 0* 0*!2+!4.&96.B=5K@8PF=WJAZKC[NE]PH`SKcVMgZRk^UoaXraXt`Wt_Vr_Vq`Wr_Vr]Tp\SoPGcI@\NEaKB^LC^QHbȇ}h_sjb|to}{~stil^`rWWiPObBCZ?@ZHJgVXv]bekiolpkqlqnsnsnrmshqemcjcjelhoiphogmfkdichbgbgaf`e^e^d^b^`[^Y\{UUqPOgNG\L@QK?Q;;N8:J59F26B/4A-2>*/7%)3!&/$-&,(, )-",-"-+".*!.*!/* 0)0* 1*"4/';7/F=4NC;TH@WJBYMD[OG^QI`TKdXOi\Tm`Wq`Ws`Wt_Vr_Vp_Vr_Vr^Uq^UqPGcB9UMD`NEaMD`NE_Ʉ{xmw|}}|{{trhg}\\oTTfQQcHGY>?S@AXJMgWYw]bdjiokokqlrnsnsnrlsisgodkdkelhohofmekdichchchbgbg`f_e_c_a`_`\]Yv[VgZR^\NWaMQdLNfMPiPRmQTnSToURpUOqSNqRNpPNkNJkNJjNJhLJfJIdIGbGE_EE]CC[BAX@@T=>Q;=N9Q:;M67F/0>*,7%)0 &.&.&-!'-!+,#-+"/+"0+"0+"2*"3+#8.&;4,C<4IB:QG?VJAZLD^OFaQHbTLfXPi\Sm^Tn_Vq_Vq^Uq^Uq^Uq_Vr_VrXOk?6RE>P>>UEF_OQmXZx[`agekimjokqlrlqkpjqhrhpfmfmfmgngnbj`f`eafbgbgdgdgdggekbmaypaws`t{boigminipinembldhcd^`YYPyUKwQKrOJoOJmMJkLIjKHjKHgJFeIEbJDaIC`GD_FC]EC[DBYB@Y@?W>>U<=R:;M68E14;*.1"'.%-%-)."-.#/-".+#/)"/*"2*"5,$91)?91H@8OEP8;G/3='+6!(2 (/ +0",/$.-$/+#0("1)!2+$7/(<6.C=4LB9RE=WGA[IC\MG`PJcUNhXPk]Tn_Wp`Xq`Xq`Xq`Xq^Uo^UoME^E=VKB\MD^PGa•xke~fa}pmxvfe|NNbLL^IHXGGTHHWJI[ONaQRiVWqY[x[^{\`]bbhgnjpkqkqkqkpjqirjrkrkrjrjriqdl^e\b\b^b`ceeherdxisØȗ͔ϒϒ}ҕٚݝߟߡٟ۟՜ϕō}pnbcWWMPFvMBpLAoLCkJDhJDfJDeJDeJDeICeICcICbHBaGA`FB_DA]B?[A?Y@@W>?S;6MB:SE>XFA[KD_NHbSMgVNi[Rl^Uo`Xq_Xq_Xr_Wq^Vq]UoOGaE=WNE_MD^PGavlke~wr|kiSRiNLcNMaHGYIIWLL\RQcSSeSTk\]w_a~]`}[`\bbhinlrlslslrlqkrjskslsltltjshren`g[aX^Z^^`cbwm`sfqÖԠݥߣᢎᡋࠈᡉ复福騑觐⤎࣍ݢ۟՛͓vyjk^`SWKyRFuODoLDkLChKDfJCfJCfJCeIBcHAcGAbF@aEA_C?^B>^B@]B?\B?[A?W>=M67?*.6#(0 &. '."*.$.,$0)$2("1)#4,'82,?92G?6NB:SD=YHA\LE`RJfWNiZRk]Tn_Xq^Wr^Wr^Wr^Wr\VoQJcG?XQHbNE_QHbĐ~s~w|usgeZXpPNeJH`HF\KJ]RQdYXlVWkUVm^azac^`~[_~\acgjootpuotnrmrnrmsmtmtmtmulsjqhoci[`V\W\z]]xg]oublvvۥ뫔ﬓ꧑襏福秐㥐ᤑ՚ǎptfk]aUYLuPEpLCkJCiJBhJBgJCfJBdI@dI@bG@bE@`C@_B?`C@_B@^B@]B?[B@U=7PB:TG?YJB\PGbTMhYRk\Un_Xq_Wr^Wr]Wr^Wr^VpUMfIA[UNgSLeRLe͜u~wwrhefcZZrMLdJHbLJbNMdWVl\[pXZnVYp_a{df_a~[^~]`cfkoqvrwrvqtosotpuntmtmtmtnslqjpdj[`V[}W[v_\ro]krsף꬘ﮙ評ڝː~s{krbi[[NxQEqMCmKAkKBjKChJBfJAdJ@bHAcFAbEAbEAbDAbC@`C@^C@]C@Y@?T>=O:;C.26$(1#)0"(/#,,$.+"/,#2-#5/&82+?6/E:4K@8QF=UJA[OGaRKfXQj[Un_Wp_Vr_Vr]Wr^Wr_WpZQkKC]TNgVOhTNgҞzpunkfdahgaa{VVoTTmTTmTUkYYo\\sZ\qY[q_b{gi_a[]}\`cflqtyuzsxrwpupupupvovnvmunskpjodj\aUY{VXs^YlzenΝ檛몔ۜ̑~upwgj\`S|UHsNBoLAlLBjKBhJBgLBeKBfJDdGCdGCdFAcD@bD?_C?_C@\B?XA>T>>O;=B039),0"&0#*-$,,$.,$0,#2.$50(:3,@80G<4MB:QJ@ZMF_QJcXOi[Sl]Vo^Wq^Wr^Wr^Wr_Wp]UnOG`QIbXPjWOhқyovormnjonoqikcd~``y[[t\]u^_v]^u\]vad}ik_cY]}\`ehosuzw|uzswpupuquqvpwovnunskpinekbf[]}XVoaXfnrإ쯜ꨐ۞˓}wpvek[aQVIwQCrOCnLAlLBjNChMEiLGhKGgJDgICfGAdE@cDAaD@_C?\C?ZB?WA?Q>6MG>XKD]PHbWNh[Rl]Uo^Wq^Wr^Wr_Vr_Vp_WpTKeNE_ZQkYQjϛw{}{xvtstkmdecc`azaa{bc}bb{``{adhl`eW[z^afipsvyx{vyuwrtrtquqvqvpuptlqkpjohmfkaaaWmp^gzw×ݤ쪗ﭓ秎ٝɐ{umudl\bSZK|UGvPEsPGpPDnOGnOImNHlLGjJEhICgHBgHBcF@aD@_B>^B=]B>ZA>R=5-D<3KD;UJ@ZOF`VMg[Rl]Tn_Vq_Vr_Vr_Vr_Vp`XqZQkOF`YQj[Rlϟ||zxttjkeeeecc~cdefdfcdceim`fV\{^bgjpsvywzvytwqtqtrurvqvpuotkqkpkojokpkjocvkozȖܠ夐馒쩓쪓ﬕ煉璉쨍駋⣈֛ʑyr~jucm]hWbRZK{VIvTGtSIsQJqOJpNHlLGjKFiJDgHBdGAaE?`D=_C<_C=^C>ZB?V?>L798(+2$+3%.2&21%4/$5/%70&92)?90G@7PH>XMB\SJdZQk]Tn_Vq_Vr_Vr_Vr_Vq`Xq]UnSKdWOhZRkľ{zuvopnnhgcddefhehdgceimagW]}_chlossvuxuxsvruqtsvsvruountkrlqmolpnrupp{|ɔϔԔӓ}ӓ~ԓ~Ӓ}яzόxΉyψxчvцu҇wՋxڐzޖ~♁~{|䛀坂碆䢆۝Ә~ȏxskxdsan]gV_Q|YJ{XK{WMyTMuSMrQKoPInOHkMFgJDdHAcF@aD>_C=_C>]C>\C@XA@E236'+5&.5(34'51&40%5/&6/';4*@<1JD:TJ@ZQHbYPj\Sm^Uo_Vq_Vr_Vr_Vq`Xq_VpXOiXOiZRkש|yxvutvvmledefgihjdfbdilcgY]~`dilnrqusvsvruqtpsrusvruquotmrmrnsosvst~}Ȑ}ƊwÄrl}jzhyfucq`n`l_j[k]l^qbwd|h͂k҅lӆlՊn؍rڏu{䟂ߜٙ}ѓzɎvqj~hyeq`hXaP_P\PYP}XQzUOwTMuSLrRJnNIjLFhICeG@bF?aE?_D?_D@[C?P;:>*.7%,7'17(55'42&5/&5.%71'<5+C>5NH?YPHaXOi\Sm^Uo_Vp_Vq_Vq`Wq`Xq`Wq\SmYQj[RlԪ{xxuwuxxqpgffgikjleg`bjlegZ\~`dilnqptruruqtprprqtsvsuruptpsrsuuztrvwyŋ{t}nxhtepal]gXdWaU_T^S]R_SbTfWjYp^vb|eςjӆm؋qڎtޔy~~ޚ~ז{Гxǎtpmk{fr`jWgWcV`V^V[UYSXR{WQxTOtQMpMIkKDhHAeG?aE?`D?^D?W>=J36;&+7&.8(38)67)82&50%60&81(<92IC;TLD]VMg\Sm_Vp`Wq`Wq`WraXq`Wq_Vp\SmZQkYQj̤|xsrssvxsuklgijllngiacjljl\`^bgmlqnrpspsqrqrpqqsststrurtssxsuu~uvxpyys{mugrdm`i]eYaV^T]T\R[Q\Q^R`ScSdTiXq^{gуkֈnٌpݐu{~ٗzӕy̑uōrpnjzeq]jXgXcWaW^V]U\SZS}XQyTOuPKpMGlJEgGAdD?aC>^B=X?=Q;:E039&+8'/;*5;,96)72&61%71&:5,C>6OI@[TKd[Rj^UmaYqbYrbYsaXr_Vp^Uo[RlXPiYPi̦{wssstuxwypslnknnqkmbdjkmo^b]bfljpmpnqosqrqrpqqsstttutwsyqrwwxwmym{{t{nuhqdlai^e[bY^U]V\S\T^T]R]Q_Q`SfXl\tc}iԅlڌp܏sz✀ܚ|֗yДvǎqonkhwao]l]h\e[cZaX`W^U\TZS|VOwQKrMGmIEiGBfE@bC@[A=T>*-8&,;(2=,9;,:5(62&61&81(=:1ID;TQH`[Ri^VnbYrcZtcZtaXq^Uo]TnYPjWNhZQkѲ{vxuyxxxy{wzrvnrrvuwghjkopbd`bhjlnmqnqososornqpsstusws|qqs{zxyqym}n|ÓŐË~w{qvlqhmekcg`e]b\c[c[b[bZ`X`W`WaWeYj]tf}l؇sޏwy}㝀䞂➀ۙ{ԕwʎrnnkk}htcpak_h]f]d\cXaV_V\TXP|TLwOIqKFmIEjGCgEAaB?[?7/DF>UWNh_VqcZtd[ub\u_Xq[TnWOiTKeTLe]Tnѱ~vyt~{xzy{}~vxnpoqhjeghilmmpnrososnsnrpsttxt{vvxzz}t}p}or~Ĕǒō}xztvqsnrnsourxu|xxxv{sxpunrkqhsgzmԁqڋy}嗀蝄衇颈碈֕yʍrnprrn|iudo`m^k]i]h\h[gZdXaT]QWM|TJwPIsMGnIDiFAbC>]A=V=Z?*-9(.;,5>0;=0?8->4*>3+?91FI?VYNhdYud[ucZt`UrYPlRKfNGaOFa[Rm˭}sh~|z~yzstqrhjhilnnrosptptnsnrrswu|vy|~~zxtssw}Ž‹|}wwrqllhiejfnkqoyvā~͈Ӌۑߓ䖓⒎܋ׅ}ԃwՆwۍ|ᒂ晈螋顎砍ᝈՓNJtlkpuun{jvepan^m]l^m^l^m^l]j[eX`U]RZN}TKwPHrMDmGAhC?a@)1C1=A3D;0D7,A7,B<2HK@XYNgcXsdYt`UpZPkSJgMC`NDaYOmȾzldw{~y{nokmmnnqptruruqtqsrtzwz|zxvr~mrdocqfqhnec]XRNIHA|D>x?;v:8v97w98{;9~>9?9C;MFYSc\oh}wІՋ؍ڐݕߘޙ֒ńqvcq_o^o]takn}jzgvbtar`q`o^o^o^p_p^p^o]n]k\hZcV]RYNySHsNCmH>gC;dB=`@=X;:I/1;$';%+A.8D6E>3E:0C8.C9/EC8PQF^]RkaVo_Un[PkUJfLB_MC`ULi۵rhuyq~}vxopooorqtsvtwsvrtts|v}}wssp|kl^eYdZbX^UWOOH{E@wA7=6<4>5C;QI[RaWg[navgwgqagV_N_OaQgVn\vcxewdwdubt`s`sar`q`rarbrbrbq`r`p`l]hZbU^RUJxPEqK?kF^?%)?*2F6AD6D@3D;0D9/E=2JG=UVKc\Qi_Tl]RlXLgMC^JA\MD_՜pgt{t~z{stqrqssvuxvzwyvvxtyzuopp~lp`gZdW`V\RYOVNTMSMRMSMQMNIPJQJQJRJQGOELCI@E;D:E;G;I>J@K?M?QATDWG[KaPcReSiWo\s_ububvctbububuctducucufvfudvetfpam^gYcVYM}TGvNBoI>jF>fD?cB>]?U95H.-@+.E29L;FN?OJ>SE;P@6L>3KA7NE;RJ@UNDWOFVI@M82=Žxsxjh~rqx{~ܲƍ~ysnzi}m}ĒĒĒȕɖƓvxnmdh^f\j_xiʉzږ術礼먑駎棋䡊⟋ܙՕГɑ|Ɛ{s}ir^mYlVoYt^{ekÉsǍw̏xΐyϑwБxБxϑwϐw̏sɍsƉr‡pl}gwct`q_o]kZfU`N[JzSCuNBoKBfG@Z=7I.+A,-E05L9BPBNKASG?RC:NB8ME:PG=QI?RLCRF=J5-7)",ɽ|tyjf~qnvu{{즤ysmyhyj{ȖǕœÑÐ}uwjpej_f[cYcXgZwi͊|ݘ垎짔ﬗﬕ쪐駍覍礎㠌ܜ֚ΗƑ{mt_nYnXpZv`}fmÉrȏw̑yВ{Ӕ{Օ{֖|ח|֖{Օ{ғvϐtˍsNJqmhzevas_r^n\jWdSaOXI~SFwPEqLDeBQ@LNCTJAUG=RFK<3?2)3'(&%%$ϼyyk`qhvo|t{͓ȤʟǚĔ~wr{hudsŔÑzxw}osdk[fWeYdZdXaU^S`Tn`qҍۘៈ祋멎몑榎ߠٝ˔|mw`mUmUu^h‡pɎv̐vΑvГwӕyזzٗ{ٗ{ۚ~ޜޜޜ~ܙ|ؗzՔxБtɉnh~ezbw`w`t_r]nZjWcQhVse~ryqc^b>:K/-E,0J8@PBPODUL@SLBRH?M<2=1(1' (&%&#%#}zl^pevn|ryΨϣɛƔ~xs}jvdoœÐwt~oufl]gVeVeXdYdWaT_U_SbToa|nϋzݚ椋멎쩑騐㤋ڝϔ}qx`mUlTr[|dĆnˍuϑyғyԔyՕxזxٖyۗyۗzܚ|ߜ}ߜ}}ߛ|ۙ{ؖyӒvˋn…he{by`y`v_u]q[lWgRxdƑǔyYUU64E*+C05N@JPDSPCSMBP@7A2(0*!'& $%"% $ğ{m_~pdvm}ry؞ĦѨФ˜ɖđ|wnxfyi‹|ŏupykpbk\gXeVfXeZcX`W`V_S_RdVj]ziюy馏ꨍ駌祊䢈ۛϐy„nv`mVkTr[hƆp̋tҐy֓|ה{ٕ{ٖyڗyږxۗyۗyݙz{}~}ߛ|ژz֕wΏpƉje}az_z`x]w]s[oWjSnڟ؞ИǑvpgB?G((C,/O>GRDRQDRJ>J6+4,!)(#'!%$#ŧ{m_qfwm}ry󺵽ˬө΢˜ʗȒ~Í{szipb~oxrmzhr`m\k[jYiYgXcW`W_T]Q^R_QaSgWxfҎ|⟊椊㡈ޜؕ~̋sku`lViTnY}gdžqЏyԒ{ד|ڔ|ە}ܖ}ۖzܖyܖyۗyܗyޙz{}~}{ܛy٘xБrȋkeb{_|_z^{_x]tZmSiٛ۟ٝї}wPJH'#B*(O0$** &) #( !&##Ū{m]~qcui}pw}羲ΰ֫ΠʙȕǑ|ōzszll_o`zinn|k{jyixgtco^hZdX_V]R[Q\Q]Q^R_QeTzhהឆٗ~юvɆo|fr_kXhVlZycƅpώy֓~ٕ~ۖ}ܕ}ܗ|ޗ|ݖ|ޗzޗyޗxݘxߙyz{|}{ߛyۙwԒrʋl…ea|_}`|_|_y]vZpT}cΎvѐzҔ~є_VK(#B)&K7:TBJL=E7*2* %("& %$#"ǰ{n]~p`ug}ov|ùϱժ˝ƕ~ŐyÎwŠvmtdj\iZveop†sÇsÇs„q}kwdo\jZcV^QZNYNZN[O\O]NiZl΋tȅojxcmYhTfTjXsblΉuՐ|ڔۖ~ܗ}ޗ}ޘ}ߘ}ߘ}{{yߙxxyyz{zߜxܙw֔s̍lÇhc~`~`}`}`{^w[uYw]ÁhŃjńmņsË~lbS.'E)$I34R?ED5;0$))"(!&%##"ȸ}p^~q`wf|nu|շҧƘxsroyfo]k[o_p͐~ΒєҖӖғ~΍yDžq|iudm^hYbU^Q]P]N]N]N`PjXr`vcwcq]hTcPeSo\yh˄qчu֌yِzړ{۔zߘ}♀䚁䚀}zyxwxyz}{yݚvוsϐpƉkea~`}`}`{^x\vZuZ{b|czc|hć{zph=6L,%L32N:><-/, #*"*!'%##"Ǽ~q_}p_wf|ntzںФ}tpo~ktbp^o^nѕ۠ݡߣ࣌࣊ࢊߞڗӏ|̆uo}l|kxhufral[gVdRdQiVo]q^mZfSdQhVsa}k˃p΄rІsшsшqՍrܔy}䚀圁zxxxxyy{zxݚvٖrёpȊlfdc}a}a{^x\vZuZza|cxayeŇxRHU2*R75L688(),!*!* '%##"r`{m]tdzmswݺϢ~tnmzitcq`tc͔歜쳢ﴢ褐✉ەٕؓݙݚږόxÀmr_fTgUlZm[jXgUiVo]we}kmʀnˁn˂m̂l΅k؏sߖz杀~{yyyyyyyyxޛwۗtӓpɌlÈifd}a|`z^x[vYtY}c̈oƁk{gÄtˑqdd@5V:6L558''- , * '%##"ķua{n\tdzl~qv~޹ΤĖÑ|upzivfq`yhۣ°°¬«êé쥐꣐ᝉˈum[gUiVhVhViWq^we|jmǀmʁn˂n̂m̃lφnՋrݔy~~}{yyyxwxzyߜxݘu֔q̎nƈkgd}a|`y]w[uYtY|bԑxܗ̈unɋ|x~UJY94I0-8%$. -* ( &$$#ʽwb{n[tdzlqu|ȿ໧Φɝ͜ϙɐs{jsbxg٠ƳϻѼѸѷѸеɬ¦ƯȲٖq^dRdQeRjWucĀnłpǃqDŽr̆sщtҋtӋtӊsԋt׎uڐw{|}~|yxxwwxzyxޙvؕsАpʋm…je}a|`z^w\uYtYz_ѐu餎ݙ~myjŠ{pd_;5H-'6$#/!!- +!( ' %$#yd{n\td{l~ptz¹ȴšskkk}ixevct`wdӘǴվھҵƪ«˳͵ɲ֖o\`PaQcRr_ńqЏ|ҏ}Ӑ~Ԑ|ג~ڔ~ەݖ}۔{ّwڏvِuڒuߗx{|{xxxwvwwwߛwޙvږtґp̌mŇjf|c}a|_y]v[w]}cȈnᜆ❉̊yugqtsKDH+%4#"/"". +!+!) !' % $ ƭ}g{n[td{l~ptzŲ}›ϥ٫کҡxzfn[lXsٽβƪɰʱ驒ÄnfS^KbPn[lΎ{٘ݚ㡌壎褎ꥎ餋✂ޗ}ّv؏sؐrۓuޕwzzwwxxvwwߜvߛwߚwۗuԒqύoȈke~b}a|_z_x]|chfˉrٖԓăsyjwi^L-&7&$2&&2$%/#%/#&.%'+%&*$&+%)ɰk{n\tdzl}orvҿһӲܴ罤ǭӸӹϸî⨕ϗҘپ˲©벚֛kkU]GZG_KnZzfLjuՕޝ䥑堅ەx؏s׏qؐrّsܓuߗvwyyyxyyxߛwwݗvՓrϏoȊjfb}a{by_w^{cix`w`ɇpѐ|Ɉv{kp|q^;6;&%7));..>14=37>4:>4;=3:<6<̳n{n]tdyk}oqvñɶƥ֮龤˯ָ׼ֽͷï쵠豜ҹū۠†mqZjTlVw`~glon~j|h|hkĉr՘쬔먌ᜀؔvؒs׎p׎pؐrۓtޖtwyyxyyxwwݘvוtёqʌl…fc}`z`x^v\{blv_gPr[łlȆr}k{kzsNH;%%:**D55J=?KADKCHLCJLBJICJεr{n]tfyi{n~quݴ´ׯ¥ΰظڽҼʴŮìؽҺ٦՛jfÆmȋrє{ߢ欒쳙ﶠ겚ۣȎu|cz`ώtꪍ覊ޛ~ڕv֑r֎p֏rّsܓstwwwxxxwwܙvٖuԓtϏpƈhe`{^x\v\|dÆou]_GcJt[}f|iyj}b[B+(;*)D45NAASHJQIMPGMPFNNGPϷv{n\tdyj{n}qtĺԸׯ¤ίضܻݾپջεɱǰҸҼǯ§מigʑt՜ۡ媐ŭɱʲʲ˱ʯʮɭܠ}bnRӔv駉ܙyבs֏r؏qڑrݓrstߘuwxxwxݙwږuוvёrʌmf`{_x]v]}eÇpt]U?U?fQs_wcue{umO61<('=//J>?SIKSJNQHOPGQOHQй{|o]tdxizm|p}r૚̺ũ{ٲ¦̯ҰѱϰϳеҸϷ͵ʳ̵ѻǯݤly^h̕wڢ毒ĨʮͲϳѴѵгαΰΰΰ̯ʭç؝|^ޞ~ﱑ祆ݙzבtِsڐqۑpݓqߕssuwxwxߙxۗvהvґrˍn‡ib|ay_w_~fifPL8O=_NlZr_rayu^@:>('7,+C89QDIUJQTJSPISPJSӽ|o]ravgxjzm|oz̽λ|἞Ǩǩ߮ԣӟ٢괛̴е̴ʲϻí魔‰otZx]k֡沒ɫѳ׸ڻ۽ܾ۽ٻָѲЯήέήΰħ۞ąd쩋㝀ݔwڑqڐnۑoݓqޔrߗtwwwwݙvڗuוvґsˌoÇjc|`z_y_|cqZT>G3Q?^MjYp]p_v|nMHC+)8-+=12K>DSHQSJTRJURJUշٻܿ{n]}p`udwgxizl~rɹ{ɸ}ɬȩ缜جșwmjhĊp٠˳͵・ҷ̸Ε|u[tYb͗w汐̬аնۺݽݾݾ۽عѳʬåŧƨŨbᢂȧ¥﬎~ݔtېoڐmۑoܒpޖsߘuvvߙuܙtٖt֕tґsˍoÆid|`{_z_v]^GK4H4TA`MjXo]o_~tuRMI.+EOGNQIQ۽ٻָҸз˳ymZ{n^rdtguhsf{k^vfY{mɴ|h̭߶ԩƙpj}h{dzcze{jrwco\ܼکjWkT|b֝ʫͮаұԲӲɩ᧋՗}ΎuȇpÃlg‚hĄmNJq̏tʍqϑtɈmW>sX¡âyےn؏jՍjՏkבnٔpڕqڕq֓pҒn͏mƊhecv[pZlVgP`JWBUAZHaOhWk[k^skzc_V63H61C62?33>57B:>KCIOHN}p^zm]~pctguh~obwgZueXwjugk[n^q`p^˴⾦޷өpk|izg{hzixixhfVyP>q׿޻we[Gh֝ħȩ˫έήͮå䭐՝̐vƈoƒlj}g|ehnŊsҖ}՚՚ÆnU?KBEOFKös`yl\}obsftg|m`p`Sl\Owi|noap`rbpavf״ծowexhyjzl|o{nxkodXLpI:vSAiV|h|ggSR?eQΓ}벙£ťǧĦ﷚ۣɐvˆpnl|h{fzfyezf|glŎx̔ϖjM6fOۡ£ĤŤƤáuԎkΉëd̈eϊgΌi͍iˍjŊihdvZgM\CaJr\r\cN]H[G[I`ObRaSd\rkfc_?AP>=M?=N@>PCBTHHTHKTILǻvbyl[}pasesezl_l\PdSGob}qtgtesdnahYqέơtrap_sctfobl`l`i\`T{QElG9iF6jG5mI5qI4sG4~M:{g䪖⨌Жzmj~h}i{h{h{g{hzg{g}ijknss^t@*dNԞäţšġꦂڕr͊gȆcDžcƄcńcņdÆeef}aoT^DS:aJwb~hp[bL[GYF[J]N]O_VlefddEHUEDTFDWIG\OLbVTaVV]RS˿xcxkX|o_rdre{nao`SdSFhVJr^TzeZ~i[l^k_fXzjwgfVhWk[k]]QUI}SG|SFyODqHmD8hA5iB6kC5lA4nA1qB1XGҠ﷡鱙נlw_pXiTdRaQ^O]O`RiYl\iY\LVFWFZI_MaOaPWDkD2oI6[Hva”|۫£ģ¡ﲓ㨈מ}ʒtif}by_w]qWgO[D~N8{I4VCsaČw΍x~keRWDVDWHWKYQf_iftVXbQPaSQbURcVScWTaVT_TRݿ۾ټĬ~iviVzl\|o_rbseth̿ĹzrxbZs[Qw]PfWgWcT]O[M[O~YLvOCqJ>nH;nIyQFZO^R]QWISFvE6rA0R@z貞춡㬙˕jr\iTcOaP\NYLVJWJZM`SaS\M}SDvL=sK;tK;xN?{RByOAoI:iF6jH8kF8nF8zQ@fT~kėۮ꼤춡૕ҟ’ykgydt`jVYE~Q=wJ6vF2Q?l\wטҎ|tb[ITEUHUJXPe]hetXYfUTdVTcVSbUR`UP_TP^SPݿݿݿݿ޿ܾټ׻ŮmtgUyk\|o_rbtgwlǽǰn`m_xj|nxlj^}VJuNBtMArK?tMB}VJ`TfZgZi\pdnbVIwD3|J6o\xŒ{rualYeRcSaR\NYMWJVKYM]Q_RZM~SEuL>tK=tK>vM?wL?uJ=mE8hD7jE:hC8d?3c<1f?4kD8tNA^Rrdw̠٫۬էǚxq~kvchUTAuJ7rE2qC0xG5`Mrޡ䡏̈wjZXITGUJYPd]cbpWXgUUeUUbURaTO_TN^SM\QMݿݿݿݿݿݿݿܾۼػչȱtsgTxjZ|o_qbtgxl|ʻzqn`aS]PWI}UG~VHXJXJXJ_Ooaċ}ΕvcQJ8I7WGcSlZhXeTeVdW_RZNWJUJYL\P]Q\OXK}TFzQCxO?wM=uJ;rG9kB6gA5iD:gB9c>5b<6d>:hA?iB@kEBmEBoH@sJ?xNAZLiZvhs{{{qm\zQ@mF4lB2nB2sD3VD|iٞ⡏łrcVVKTL\Rd\|]]lVWfTTcUVbTRaTO_SL]RL[PKݿݿݿݿܾڼغԸɱxreSwiY{n^}p`sdxk{Ωǟ}wt}ovhn_dU^O[MZL[L[KbRo_qԕ۝ÈvkWWFJ:zG7|J;M?QDWJ[N\PZPWMWLYN]QaU`S^QYJUD{O?yL0h>3kD9iD8d?7c=:f>4`;.];,a?2qPBdUzlxrevQDlE6jA2lA3qC4xI9bRÉ{쬞~ocXZR^Tc[w\[hWVdVUcVU`UR_TO_QL]OH[MFܾܾܾܿ۽ڻ׹ӷȱ}sfSviXzm]}p`rbwhw˿ӿ֯өȞvroxhraiYbS^O]O_N_NfRq^|iĂpƅryfmYdQ_NVEQCQDTGVIWI~OCvH7g=:f>;c?;d?1Z;-W9*V9+X.^9)b5'j:+SAtԞ֝ȏtjaWoVNfUOcTLdTKaRFZM@RC6G7*D5'ۿܿܿ۾ۼڼٺععոѵɱvgSuhUxjZ{m^~pate|mɼϻ۸߹߶ۮѢĒviv\nWkWkYlZp_sbt`{ëqۘ䠇袊垅דyLJnw_jUiUgUfUgXgZfWbTaRaRbReRnW{dnɎu̒wҗxך{ޟៃ⟃ߠޟߟកߟݞ۝כ~ך}ՙ{̕vnew\iO\ExL6h@-`;)[7&Z8)U7'T7)W9,X:.Z<-]?0]>0_>0c>/d=.f=,wO>eSq`tceVuXInUIhUJhYLqbSyjYtep`sbSugVڿڿڿڿڿۿ۾ڼٺٺع׷׷ԵѳȯvhSuhUwjZ{l]oate{kϼ˯Ѭ濠潜輚㳒צɗym|eyexbyc{h|jiˉqߚ짎﬒饈ݚ}ώq~et\pYoXmYkZjYhWfSgTgTiSnW{bɌrӗ|۟ݡ㦄髉몇쩅먅꧅駅观~{wtޜuܞwڞwלwϗrɒsqmydfTtO?bB0\>.\=/\>0\?0[?0]A1^C3_C2`B0`@.\=*bA-{YDmZlYmY{jzʺŲıͽ˼ڿپپپپپپٽٻععֵָյӳвǭwjUtgSviYyk\~n`rcxg|ï̹Ϲ϶ƪӰɩ˪̨Ǥンᰑբѕ~̎wƈpąmȆp͉tѐwߜ쪋䢂ٗyˊme|byavar_o]lWjUoZs\y_dʌoי|ࣅ骊뭌נּנּ~{ywvvݞvםvԜxЙzʘ~ÖvlYvSAiJ9fG8cF7`E5]C3]B2]B2^B2]A1\A0Y@,`G2zeɲιĮɴɵů¬Ƿڿڿٽؼؼؼؼؼؼ׻عָָԶԴӳҲͯƬxiUseRwgXzj[oasdwfoDZϷѶ̭ճ˪ӱشصճάᣊ՗}̍sҐxڕ⟆ﯔ쫊䣃ؗẙodžlÃkh|fvas]s\zaiȊmѓtߠ驉}}}zyywޟw۞yٜ|ӝʚrjVvS@oLqM>nM=iL=eK:cH8`E5]E3\E3^E4^G6]I7dQ>vٽؼֻպպպֺֺպԹӶӵӴѲЯͬ˫ǩ{lXqbOufVyiZ}m_patboͷҼӾԸʨƧϮϫѬҬҮͬäǥţĢ ⣃Օwϑtϒsҕvٜ}㤅誊믎Ýʣʣɡȟȟ˟ʝǛę–见䦁⥃ۣœuw^\GsOmN=iM=eL;aI8aG7^F5]G5^H4^J6]L6eT>uٽؼֻպպպպԹӸҷѴѳвϰέ˪ɪŨ}o[qbOueVxhZ|l^o`r`j­ϺǯٿǦϮΩ̨ʨ˩ǧ¥ŦȧʧʦʦȣƠƠğं՚x՛xݤ걎ĠǡɡʢͤХͣʟȝȝȚǙŘŘė֕髆䧃ݢ˕vgkRWArN;nO>mO>jN?fMlN>iN>eLoN:lM;kM=iLiH6gH6fK8fN:eL;bJ9_G6]D3ZC2XB1YB1XA0WC1XD0[H1pYͳ̲˱ʰɮɮɮɮȭǬƬƪƨææ¥̾ôpm^Lo_NrcQwgW{l\o`sdt³ïλƱv`޻⼝د—y|^w[y_zav^qZoW{bąkՖy뫋쪃ȡѨ֬ڭܯܮڭجث֩ԧУ̟ʜʛʛɛȚșȗǖŔ“⣁ԛyŒndsXkR_H|UAmJ8fF5eG6cJ7cM9cL9aJ9]G6[E3ZC1XA0YB1WA0WC1WD0_M4y_̲˱˱ʰɮȮȭȭȭƬƬūŪç¥ϿͿ˽õrl]Ko_NrcQwgWzj[~n_rcrɶƳ}hٷرŞirYsZt^t_oZjVdOfQmV~fז{ʣӨ׫حججתשԧҦϥ̟͢˝˝˝ʛɛɚȘƖÓꪃޡ~̔rfvZlSbJXCtN;hH5cF3cG5eK8cK8bL8`J9]G6[E3YC1XA0YB1WA0WC1WD0eS:f~˱˱ʰɮȭǭƬƬūūūĪ馥ϿͿ̾ĵsk\Jm^LqaPufUyiZ}m^qbqʽŲǵrϱȥpoXiRnWp\p\kXdR`LcNjTt`ņnښ{ɠΥѨӨԨըԦҤХΣ̟̝̝̝͡˝˝˝əƖĔפּٞzǐncvZiO\DyS=nK7dG4bG4bH6bJ7aJ7aK7`J8]G6[E3YC1XB1YB1WA0WC1ZG3q`HrʰʯɯȭȭƬƬūūĪĪĪèο̾˽Ķwl\Km^Lp`OueUyiZ|m]pa~qɽƴɸ³utyvaaJ^HdOgTfTcRZIWD_JiTq^|eŇjՕx祆śʟ΢ѥѤϤ̠ͣ˞˝˛˛˛˛˝˝ɛǗĔ뫂|֜xɒpfw]fNY@vP9lI5dG3aH4aI7`I8`J6aK7`J8]G5\F4ZC2YC1YC1WB0WD0\K6xgPyɯɮɮȭȭƬūūĪĪéé§ο;˽ɻŶ|m^Lm^Lp`OteTxhY{l\p`}oǼŴʺƷsvd~kxdhV{YEyVC[H^M]NYK|SE|PAZHcQkWu_~diϐt奅ÝÝÛÛŞɠ̢Σ̢ͣʟɜʝ˝˝˝˝˝˜ʚǖē쭃~ٟ{Лxlw]fN~V@rN9jG5dG4bH5aH7_G6`I6`J6`J8]G6\F4[E3ZD2ZD2XD2YF1\K6}lV|ɯɮɭȭȭƬūĪéé訧пξͽ̼ɺǹŶn^Mm]Lo`OtdTxhY{k\~p`|nǹóʺ˻u~iWiUeQ~_NsRBpO?sSCwUFxUFxSDuMAuL@TE[I`OkWu^zdk՗|쮎¡ŢǢȣȣɢʣˢ̢̣ͤʠɟʟ˟˟˟˟˞˜ʚǗŔﰅ魅ᨄ֡sv\dN}VBqM;hF5cE4bG5bG7aG6`I6`J7aK9_I8^H6]G5[E3[E3YF3ZG2^M7s]}ɯɭǬǬƬūūĪĪ駧Ͼͽ̼˻ȹǸŶqbPm]Mo_QscUvfYxj\|n`{m÷ȹ̾zyeTw`Lz_L|^NrSCmN>mO?pQBqRBqP@oL?rL@zRCXH[IaLjSoYt^lә}鮒Ģƣȣɤɥɣˣ̤ΥΥ̡ͣˠ˟˟̠˟̟̞˜șŖ찈嫇סqu[bL}UAqM:hF4dG4cI6dI8bI8aJ7aL8aM;aM:`K9^J7]I5]H4\H4[I3cR<}gʯȭǬƫūĪĪĪéèпͽ̼˻˻ȹŷŶueTl\Mn^PrbUueXvi[zm_zl~ɾǹvdUu`My_M}aQ|^OqSDmPBmQCmQAnO@lN?qMAwSDYI[K[GZD]GcNnYkϘ᪎춖áǤǤɤʤ˦ΧѨЦϢ̠͡͠͡͡ϡϡ͟˜ǘÓ䪇ϚzisX`IzS?pK8iH4fI5gL9fK:dJ:cL8dO;eQ?fR@dP>bN;aN9aM8`M8]L6o^IuɮǬƫūūĪĪĪè§пξ̼˻ʺʹƸŶĵvfWl\Nm^OqaSscVwgZzk^xj|ȻƸŸwfWt`Nv`LgVjZz_OsWHmRDlQCmQBnQCpPCuSD~YJ[N[KSA~P>TC\KhW|jyН٥ᬐäǥɥ̦ЩӬӪѦΤΣϣТѣӤңТ͞əĔﶏ㫇˗wcjPYBtO;nL9iJ8jNkWEmYGlXFkWCiUAgT?eQ=bQ;xgRyȭƬūĪĪééé§пξͽ˻ʺɸȸƷõõxhYk[Lm]Mp`QrbTueXyi\ugyȻŷƸn_t`Nv`Ll[sbk[}cSuYLqUHqUHsWJxZLxWI}ZM]P[N|TDyN?zN?~PAVF\Lm[wb}enϚ્Ģͨӭְ֯ԪҨҧӦԥԤ֦եңϟ˚ƕ﷐ᬉŖv{]cK|U?qP;nOgTAiUCp\Jr^Ko\GkXCgV@gXB|e}}~~}}}}ƭŪĪīëϿͽͽ˻ɹǷƶŵô~o^hXGk[Kl]Mm_QobTsfX}pbrȺ´Ķreq`Ou_NiZtftfn_gY|cU{bSi[ugl^sVInQDpPDoM@jH;gE7hC5jC4mF7rK;yRAWD]IeNsY~b’uᴒִͫسذحثت٫ثګکاգϞɘŕ붓Н}~acJ[E^I~aKy]ItZFqVDkR?eL;^G6`J7fP>eQ?dP>mZGq]IlYDiVAfU?o`Jo~}}}~}||}|~~ūĪªªппϿϿͽ̼˻ʺȸƶŵĴ³sbiYJjZLk\Nl^OobRseV|n`~pƸõpbq_Os]MxbSm^qbi[fX}eW~eWl^ufk^nSFaF8^D6^B5aB6bA5c@4e?5gA4jC6mF9qJ;wO?UA`IlSx]s䷘ϯԳֱװ׮ج٭ڭۮۮڬ֨Ѣ˛ǘĕ–汑ÑtmU^I^KeQhS}bNqWCfL9aG5]D3WA0\F6eO?eQ?cO3c?2e?2iB6lG8qJ;vO=VAcLlTy^u߰Ǩӱ԰ծ֬׬ٮܰܮڭ׫ӥΞʚǘřěĝĞŸⰑpjT`M`OhUjVw]IjP?bI7_E4[C2[E3bKbJ9aI8bK9eO=jSCiSBiUBjVCfR@`M:_N9dT<}nUs}~~~~~~~~}ëªпξξ̼˻˺ɸǷŵŵŵóyjeUGfWIiZLk]Nm`RnbTuhZwi~ŷ˾so_Ro^QucVuaTo[MlWHjUEjVFt`R}gZybVmTHgNBcL?aJ=`G;`G:bE9aD7bC6cC6fE8gF9kJ;oN=rP>vR@zTD\JcOgPjQz`w߰ģ˦˦̨ϩЦЧШΦˣʠɡɢȦǦŦΞ|dfQ}\JwWFqUCoVClSAgO>eL;dK:dM;gQ?jTBiS@hUBkWEfR?cQhM?kM@oPDrSGqSEpRCpRApQ?qO=sP@ySDZH\H[F^IjRg̚|ܪ泒췖泜ĕua_LwWDqTBnS@mT@lSAiQ@gN=eL;cM;fP>gR=dO;dP=kWEiUBgU?gV?xhRs~~~~}~~}}}}}}}}}|~}xggXGeWHgYKk]Nm_RobTtfYsezǻȽwioaTtdW|i]xfZsaSiVHdRB`N>eQBp[Ku^Ou^Ps\OqZMmVIkSEmRDrVIz\P|`S~aT|`Sw[LrVGnP?jK9jJ9nJ:tN=zR?{R?}R>U@cKsXiŒs՟ੈ⫈ަנ~ԝ{њz̖vɒtɒu˕xћףΞoiW|YGtTCpRBmSBkTBkTBiSAeO=aJ9_I7aK8aK6\F2^J6mZGq]JhUAlZDxaz~~~~}}}||||||||||xk\KeWFgZJk]Nm_RobTreW}obwƻǼuhZn`Sp_So\Pn[OjWKgUFcQA`L=bN>jSDnXIs\Ou^Qv_Ry`Sx]Pw[Nz\Oy^Py]Pw\NrWInSEgLzS?YCcJnRx\gǔvΛ|̚zɖwŒtlctXrVtYy^i~jkY|YIpO?mO>nQBkRAjSBjTBhR@cM;]G5[E3[E3\G2YC0]I6p]JxdRkYEudOp|}}}}}}}||||||||||пξͽ̼˻ʹʹʹȸǷŵijò{m^OfVHiYJl\Mm`PnbSqdV|oauźƻźppbUn^RjZMhXLfVIdTGeTFePDbNAdN@eQBmXJpZMtZOy_RdV{`RsVImQDiN@fJ=aE8aF8aF9_D6`E6bE7dJ8hNqSAxSC}TC^KdMiQoWw]jqrneu[hPaJ`KcNfR`MvTBoN=kL;jMdL;`J8]G5XB0V@.WA/XB0V@-YF3mYGvcPq`Lv`w||||||||||||||||||пξͽ˻ʹɸɸȸǷƵij²|n^OeUIhXKk[Ll^OmaRqcU{n`tźƻź¶{pal_Qh[MhYLgXKeUHeSGfSGfQEgPCgRDmVInXKnVJmTFsXK|`S}_Sy^PpVHiN@bH:aG9aF9bG:dI;dJ;eJ:fLnTBqWGoUEjP@^F5YA0V@/T>,R<*R<*T>,S=+XD2hUCtaOq]rz|||||||||||||||||zϿξ̼˺ʹɸɸǷƶŴò±paReVIfXLh[Mk]Om_RpbTyn_sşŶspeVi]Oj]Ol^QpbUo`Sl\PjWKiUHpYMwaT|fY{fYr\MoTFsXK}bTeWy`Ru\MnUFhO@gN?gN@hOAjQCiPAfM,YH5kZGn[q{{{{{{||||||||{{zzzyξͽ̼˺ɸȸȷƶŵijò°scTeVGeWJgYLi\Nl^QoaTxm^p¶÷÷÷~seshZk_Qk]PqcVxk]~na}l_r_Ss]RydVj]na~gYsYKt[MeWk]}dVy`QsZKlSEkRDlSDlSEpWIsZLjQCeL=dI;hJoQA|_P}aQuXIuZJx_Nw_Nt\KrZIiP@W?.N8&N8&M7%M9&N:(T@.`M:raNl{{zzzzzzzzzzzzzyyyxxxwͽ̼˺ʹɸȷǷŵĴò±¯wgWcTEeWHfYJh[Lk]OnaSvk\{m~pxisezm^obRk]NrcUxhZveWn\NhVGdPCeOBkTFt[Ms\MqZKx`R{dUu^OpXIoXIlUFhQBfOAjREoVIoVIkREgOBjRClSDjO@iLqP?rRArTBtWDvXFy[K}`QuYHkO>lQC{aR{aRpWHpWHv^Nt]MoYGlUDfM=V>-P9(N8'N9&N:(P>+[H6q_Lvau{zyyyxxxyyyyyxxxxxxwvv̼˻ʹɸɸȸǷŵijòxiZcTDdVFfYIh[Kj]Om`Suj[zl~}ovhvgvf}o_qcSh[KgXIiYJkYJiWIiWHkWImWHmVFkUFmWHv`Q{dUu_OnXIlWGiUEgRBeOBeNBbJ>cJ>dL@eNAlUGmVGeL=bH:gK=jN@jM>kN>lO@kQAjP?iO>oUDz_OtZJhO?hQBv^Ou]NeN?kTEt]Nt]No[InWFfO=YA0S<+Q;*Q=+TB/[I6hWDp\qxyxxxxxxxxxxxwwwwwwwwvu̼˺ʹȸǷǷƶŴò±zkZcSDeVGgWJgZLj]Ol`Rti[xi|qugufueue|n^naQcUFcSDeUFlZKq_Qr_Qo[LiSDiSCjTDq[LycSycSt^OjVGdPBePCfRDdOA_H;]F9^G;aJ=fOBgPCcL?aHgM@fL?`G9aH:lSCw^Ou]NkRDiRCpZKjSD^H9jSDt]Nt]Np\InYGjSB]G5WA0UA/YE3_M:kYF|jW|itxxxxxxwwwwwwwwwwwwwwvvu˺ʹɸǷǷǷƶĴò}n^cSCdTEfWIgYLh\Mk_QsgYugztvguftdscrbyl\m`PbTD`PAbRCiWIiWIgSDbM>cM=dM>eP@oZJt]Nr\MgRCaM?dOBjUHhTFaL?\G:^H;`IcL?fOCgPChQDgQDdOBaL>aL?aK>[F9]H9hRCoZJr\MpYLmWGiRC\H8]H9lWGr^Nr^Nr^Lo[IkXDbN<_H7_L9fTAsaNr^muvwwwwwwwwwwwwwvvvvvvvvvuʹɸɸǷǷǷŵò±sdcTDaTDdVGfXKhZMm_RqeW~sdxtvguftdscrb}p`xk[l^NaQAaN>dP@eRAbQ@`N=_K;_K;`L_K=`K=bK>cL?gREjVHiUGgSCfRBcNA`L>]I;YE7[G9dOBjVHp[Mq\MpYJcL=\G8`LaL>bM?eQCiTGhTFfTCgUDcQC_L>ZH:XF7ZH9bPAhUGlZKmYKiTE_K;^L;bP?n\Kp^No]Ln\KkZHiXCgUAiWDwfSs_ltvwvvvvvvvvvvvuuuuuuuwwvvvsɸǷǷƶŵĴò±wicSGbRFdTGeWJfYLl`RodU}rcvrufterbrbqa~qa~qa{n^xjZwiWufTteSufTveTs`Pr^Nm\KlZIkWGeQA`MePCfRDgUDgUEeSEaO@]K=ZH9ZH9\J<]K=_M?`N?aN>`M=aP>gUBn]Ip_Lo]Lm[JjYHiXFlZHveRualtvvwvvvvvvvvvvvuuutttuwvuuutǷƶƶƶŵij±pcUGaQEdTGeWJeZLl`RocTzpattvetdsbrb~qa~qa~qa|o_{n]{n\ylZwjXxjWzkX{jXweSsaPp]MjVGbN?bO?hVEo]LtbRucRtbQo^MlZKcQBYG8WE5ZF8bO@fSDfTCeSCiVIkXKiWHeSD`N?ZH8VC4TB2VD4aO>fTCgUDjXFo^Kp_Mp_Mo^Lo^LueSq^}iqtuuuuuttuuuuvvvuttttutuuutttsƶƶƶŵĴò±qdWH_QDcTGdVHeZLm_RnaSvn^quwetbsbqa}p`}p`}p`|o^|o^|o]{n\ylXzmX}o[r]p\{kWwdTr_QkXJhVGlZIn]Lp^Oq`PucQq`MkYHaO>YH7ZH7bP?jYHjXGeTCbPBlZMxeXweUn]LcQ@ZH7VD3XF6bP@n]Ln\Kn\Ko]Lq`OwfT{jX|mYt`|hotustttttrrtuuuuuuutsssutttttsssŵŵŵĴò±tgWJaQEbSFcUGfYKm_RmaSum]oxvetcrbqa}p`}p`}p_|o]|o]|o]{n[zmXzmXwb~iu`}nZyiXxgYvdVp^Oo]Nn\MlZKlYKq`OucRjXGeSCfUDjXGm[Ko^Mo]LkYIm[KucUzgYucSlZIeSBbP?eSBm[KudSweUucRsaQwfU~l[r^{ekorrrrrsttttssttttttttsrqqrrsrrrrrrŴĴĴò±wiWKaQDaREbUGfXJj]Ol`Rtl]|l}{xitesbqa}p`}p`}p_|o\|p\|o]{nZylVzmXydrlyd}o^|n^{k\xgYucTp]QmZLkXJlYKm[Lm\KucR~m\}kZygVvdSsbQraPsaQtcSq_OiWFdSBdRAhVEsaQyhW{iX{jY~l\rbve}lmoqssqqqqrsssttutsssssssrqppppqqqqqqq \ No newline at end of file diff --git a/ee/packet2/Makefile b/ee/packet2/Makefile new file mode 100644 index 00000000000..6cc6e19cf44 --- /dev/null +++ b/ee/packet2/Makefile @@ -0,0 +1,17 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2005, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +EE_OBJS = packet2.o packet2_vif.o erl-support.o + +EE_INCS := $(EE_INCS) -I$(PS2SDKSRC)/ee/math3d/include +EE_INCS := $(EE_INCS) -I$(PS2SDKSRC)/ee/draw/include + +include $(PS2SDKSRC)/Defs.make +include $(PS2SDKSRC)/ee/Rules.lib.make +include $(PS2SDKSRC)/ee/Rules.make +include $(PS2SDKSRC)/ee/Rules.release diff --git a/ee/packet2/include/packet2.h b/ee/packet2/include/packet2.h new file mode 100644 index 00000000000..e8d0fb66fbd --- /dev/null +++ b/ee/packet2/include/packet2.h @@ -0,0 +1,206 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** @file Successor of standard packet library. */ + +#ifndef __PACKET2_H__ +#define __PACKET2_H__ + +#include "./packet2_types.h" +#include "./packet2_chain.h" +#include "./packet2_vif.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + // --- + // Packet2 management + // --- + + /** + * Allocate new packet2. + * @param qwords Maximum data size in qwords (128bit). + * @param type Memory mapping type. + * @param mode Packet mode. Normal or chain (so also vif/vu). + * @param tte Tag transfer enable. + * Used only for CHAIN mode! + * If >0 transfer tag is set during packet sending and + * add_dma_tag() (so also every open_tag()) will move buffer by DWORD, + * so remember to align memory! + * @returns Pointer to packet2 on success or NULL if memory allocation fail. + */ + packet2_t *packet2_create(u16 qwords, enum Packet2Type type, enum Packet2Mode mode, u8 tte); + + /** + * Create new packet2 with given data pointer. + * @param base Pointer to base (start of buffer). + * @param next Pointer to next (current position of buffer). + * @param qwords Maximum data size in qwords (128bit). + * @param type Memory mapping type. + * @param mode Packet mode. Normal or chain (so also vif/vu). + * @param tte Tag transfer enable. + * Used only for CHAIN mode! + * If >0 transfer tag is set during packet sending and + * add_dma_tag() (so also every open_tag()) will move buffer by DWORD, + * so remember to align memory! + * @returns Pointer to packet2 on success or NULL if memory allocation fail. + */ + packet2_t *packet2_create_from(qword_t *base, qword_t *next, u16 qwords, enum Packet2Type type, enum Packet2Mode mode, u8 tte); + + /** + * Free packet2 memory. + * @param packet2 Pointer to packet2. + */ + void packet2_free(packet2_t *packet2); + + /** + * Reset packet. + * Move next pointer back to base pointer, + * Make all "openedAt" vars to NULL + * Do memset on data if required. + * @param packet2 Pointer to packet. + * @param clear_mem If >0, data is cleared via memset(). SLOW! + */ + void packet2_reset(packet2_t *packet2, u8 clear_mem); + + /** + * Update current position of packet buffer. + * Useful with drawlib functions. + */ + static inline void packet2_update(packet2_t *packet2, qword_t *qw) { packet2->next = qw; } + + // --- + // Basic add + // --- + + /** NOTICE: vif_added_bytes increased by 16. */ + static inline void packet2_add_u128(packet2_t *packet2, const u128 val) + { + *((u128 *)packet2->next)++ = val; + packet2->vif_added_bytes += 16; + } + + /** NOTICE: vif_added_bytes increased by 16. */ + static inline void packet2_add_s128(packet2_t *packet2, const s128 val) + { + *((s128 *)packet2->next)++ = val; + packet2->vif_added_bytes += 16; + } + + /** + * NOTICE: Alignment alert. Size of dword (1/2) + * vif_added_bytes increased by 16. + */ + static inline void packet2_add_2x_s64(packet2_t *packet2, const s64 v1, const s64 v2) + { + *((s64 *)packet2->next)++ = v1; + *((s64 *)packet2->next)++ = v2; + packet2->vif_added_bytes += 16; + } + + /** + * NOTICE: Alignment alert. Size of dword (1/2) + * vif_added_bytes increased by 8. + */ + static inline void packet2_add_u64(packet2_t *packet2, const u64 val) + { + *((u64 *)packet2->next)++ = val; + packet2->vif_added_bytes += 8; + } + + /** + * NOTICE: Alignment alert. Size of dword (1/2) + * vif_added_bytes increased by 8. + */ + static inline void packet2_add_s64(packet2_t *packet2, const s64 val) + { + *((s64 *)packet2->next)++ = val; + packet2->vif_added_bytes += 8; + } + + /** + * NOTICE: Alignment alert. Size of word (1/4) + * vif_added_bytes increased by 4. + */ + static inline void packet2_add_u32(packet2_t *packet2, const u32 val) + { + *((u32 *)packet2->next)++ = val; + packet2->vif_added_bytes += 4; + } + + /** + * NOTICE: Alignment alert. Size of word (1/4) + * vif_added_bytes increased by 4. + */ + static inline void packet2_add_s32(packet2_t *packet2, const s32 val) + { + *((s32 *)packet2->next)++ = val; + packet2->vif_added_bytes += 4; + } + + /** + * NOTICE: Alignment alert. Size of word (1/4) + * vif_added_bytes increased by 4. + */ + static inline void packet2_add_float(packet2_t *packet2, const float val) + { + *((float *)packet2->next)++ = val; + packet2->vif_added_bytes += 4; + } + + // --- + // Other + // --- + + /** + * Print data of packet. + * @param packet2 Pointer to packet. + * @param clear_mem Count of qwords, type 0 for all. + */ + void packet2_print(packet2_t *packet2, u32 qw_count); + + /** + * Print amount of qwords of packet. + * @param packet2 Pointer to packet. + */ + void packet2_print_qw_count(packet2_t *packet2); + + /** Copy data from b packet into a packet with memcpy() */ + void packet2_add(packet2_t *a, packet2_t *b); + + /** Returns count of added qwords into packet. */ + static inline u32 packet2_get_qw_count(packet2_t *packet2) { return ((u32)packet2->next - (u32)packet2->base) >> 4; } + + /** Returns count of added qwords into VU. */ + static inline u32 packet2_get_vif_added_qws(packet2_t *packet2) { return packet2->vif_added_bytes >> 4; } + + /** True if packet doesnt have even number of quads. */ + static inline u8 packet2_doesnt_have_even_number_of_quads(packet2_t *packet2) { return ((u32)packet2->next & 0xF) != 0; } + + /** True if dma tag is opened. */ + static inline u8 packet2_is_dma_tag_opened(packet2_t *packet2) { return packet2->tag_opened_at != NULL; } + + /** True if DIRECT/UNPACK is opened. */ + static inline u8 packet2_is_vif_code_opened(packet2_t *packet2) { return packet2->vif_code_opened_at != NULL; } + + /** Align packet2 to next qword if we are in the middle of qword. */ + static inline void packet2_align_to_qword(packet2_t *packet2) + { + if ((u32)packet2->next % 16) + *((u32 *)packet2->next)++ = 0; + } + +#ifdef __cplusplus +} +#endif + +#endif /* __PACKET2_H__ */ diff --git a/ee/packet2/include/packet2_chain.h b/ee/packet2/include/packet2_chain.h new file mode 100644 index 00000000000..5e2b7cba995 --- /dev/null +++ b/ee/packet2/include/packet2_chain.h @@ -0,0 +1,240 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** @file Chain mode related functions for packet2. */ + +#ifndef __PACKET2_CHAIN_H__ +#define __PACKET2_CHAIN_H__ + +#include +#include + +#define PACKET2_COUNT_QWC 1 << 16 + +#ifdef __cplusplus +extern "C" +{ +#endif + + // --- + // DMA tag management + // --- + + /** + * Set dma tag. + * For more details, check description of dma_tag_t. + * @param tag Pointer to DMA tag to set. + * @param qwc Qword count. + * @param pce Priority Control Enable. 0 by default. + * @param id Tag ID. + * @param irq Interrupt Request. False by default. + * @param addr Address. + * @param spr Memory/SPR Selection. False by default. + */ + static inline void packet2_chain_set_dma_tag(dma_tag_t *tag, u32 qwc, u32 pce, u32 id, u8 irq, const u128 *addr, u8 spr) + { + tag->QWC = qwc; + tag->PCE = pce; + tag->ID = id; + tag->IRQ = irq; + tag->ADDR = (u64)((u32)addr); + tag->SPR = spr; + } + + /** + * Add dma tag. + * For more details, check description of dma_tag_t. + * Will move current buffer position by: + * DWORD if packet->tte == true, + * QWORD if packet->tte == false + * @param packet2 Pointer to packet. + * @param qwc Qword count. If you want to use + * packet2_chain_close_tag(), and count qwords + * automatically, set value to PACKET2_COUNT_QWC. + * @param pce Priority Control Enable. 0 by default. + * @param id Tag ID. + * @param irq Interrupt Request. False by default. + * @param addr Address. + * @param spr Memory/SPR Selection. False by default. + */ + static inline void packet2_chain_add_dma_tag(packet2_t *packet2, u32 qwc, u32 pce, enum DmaTagType id, u8 irq, const u128 *addr, u8 spr) + { + assert(((u32)packet2->next & 0xF) == 0); // Free space in packet is aligned properly. + assert(packet2->tag_opened_at == NULL); // All previous tags are closed. + + if (qwc == PACKET2_COUNT_QWC) + { + packet2_chain_set_dma_tag((dma_tag_t *)packet2->next, 0, pce, id, irq, addr, spr); // placeholder + packet2->tag_opened_at = (dma_tag_t *)packet2->next; + } + else + { + packet2_chain_set_dma_tag((dma_tag_t *)packet2->next, qwc, pce, id, irq, addr, spr); + packet2->tag_opened_at = (dma_tag_t *)NULL; + } + if (!packet2->tte) + *((dma_tag_t *)packet2->next)++; + else + *((u64 *)packet2->next)++; + } + + /** + * Close dma tag. + * In reality, get back to pointer "tag_opened_at" and fix qwc value. + * @param packet2 Pointer to packet. + */ + static inline void packet2_chain_close_tag(packet2_t *packet2) + { + assert(packet2->tag_opened_at != NULL); // There is opened tag. + assert(((u32)packet2->next & 0xF) == 0); // Free space in packet is aligned properly. + packet2->tag_opened_at->QWC = (((u32)packet2->next - (u32)packet2->tag_opened_at) / 16 - 1); + packet2->tag_opened_at = (dma_tag_t *)NULL; + } + + // --- + // DMA tags + // --- + + /** + * Add CNT tag. + * NOTICE: packet2_chain_close_tag() required. Qwords + * are counted automatically. + * For more details, check description of dma_tag_t. + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + * @param pce Priority Control Enable. 0 by default. + * @param spr Memory/SPR Selection. False by default. + */ + static inline void packet2_chain_open_cnt(packet2_t *packet2, u8 irq, u32 pce, u8 spr) + { + packet2_chain_add_dma_tag(packet2, PACKET2_COUNT_QWC, pce, P2_DMA_TAG_CNT, irq, 0, spr); + } + + /** + * Add END tag. + * NOTICE: packet2_chain_close_tag() required. Qwords + * are counted automatically. + * For more details, check description of dma_tag_t. + * NOTICE: Don't forget about close_tag()! + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + * @param pce Priority Control Enable. 0 by default. + */ + static inline void packet2_chain_open_end(packet2_t *packet2, u8 irq, u32 pce) + { + packet2_chain_add_dma_tag(packet2, PACKET2_COUNT_QWC, pce, P2_DMA_TAG_END, irq, 0, 0); + } + + /** + * Add REF tag. + * NOTICE: Qwords are NOT counted automatically, so + * there is NO need to use packet2_chain_close_tag(); + * For more details, check description of dma_tag_t. + * @param packet2 Pointer to packet. + * @param ref_data Pointer to data. + * @param qw_length Size of data in qwords. + * @param irq Interrupt Request. False by default. + * @param spr Memory/SPR Selection. False by default. + * @param pce Priority Control Enable. 0 by default. + */ + static inline void packet2_chain_ref(packet2_t *packet2, const void *ref_data, u32 qw_length, u8 irq, u8 spr, u32 pce) + { + assert(((u32)packet2->next & 0xF) == 0); // Free space in packet is aligned properly. + packet2_chain_add_dma_tag(packet2, qw_length, pce, P2_DMA_TAG_REF, irq, (const u128 *)ref_data, spr); + } + + /** + * Add NEXT tag. + * NOTICE: packet2_chain_close_tag() required. Qwords + * are counted automatically. + * For more details, check description of dma_tag_t. + * @param next_tag Pointer to next tag. + * @param irq Interrupt Request. False by default. + * @param spr Memory/SPR Selection. False by default. + * @param pce Priority Control Enable. 0 by default. + */ + static inline void packet2_chain_next(packet2_t *packet2, const dma_tag_t *next_tag, u8 irq, u8 spr, u32 pce) + { + assert(((u32)packet2->next & 0xF) == 0); // Free space in packet is aligned properly. + packet2_chain_add_dma_tag(packet2, PACKET2_COUNT_QWC, pce, P2_DMA_TAG_NEXT, irq, (u128 *)next_tag, spr); + } + + /** + * Add REFS tag. + * NOTICE: Qwords are NOT counted automatically, so + * there is NO need to use packet2_chain_close_tag(); + * For more details, check description of dma_tag_t. + * @param packet2 Pointer to packet. + * @param ref_data Pointer to data. + * @param qw_length Size of data in qwords. + * @param irq Interrupt Request. False by default. + * @param spr Memory/SPR Selection. False by default. + * @param pce Priority Control Enable. 0 by default. + */ + static inline void packet2_chain_refs(packet2_t *packet2, const void *ref_data, u32 qw_length, u8 irq, u8 spr, u32 pce) + { + assert(((u32)packet2->next & 0xF) == 0); // Free space in packet is aligned properly. + packet2_chain_add_dma_tag(packet2, qw_length, pce, P2_DMA_TAG_REFS, irq, (const u128 *)ref_data, spr); + } + + /** + * Add REFE tag. + * NOTICE: Qwords are NOT counted automatically, so + * there is NO need to use packet2_chain_close_tag(); + * For more details, check description of dma_tag_t. + * @param packet2 Pointer to packet. + * @param ref_data Pointer to data. + * @param qw_length Size of data in qwords. + * @param irq Interrupt Request. False by default. + * @param spr Memory/SPR Selection. False by default. + * @param pce Priority Control Enable. 0 by default. + */ + static inline void packet2_chain_refe(packet2_t *packet2, const void *ref_data, u32 qw_length, u8 irq, u8 spr, u32 pce) + { + assert(((u32)packet2->next & 0xF) == 0); // Free space in packet is aligned properly. + packet2_chain_add_dma_tag(packet2, qw_length, pce, P2_DMA_TAG_REFE, irq, (const u128 *)ref_data, spr); + } + + /** + * Add CALL tag. + * NOTICE: packet2_chain_close_tag() required. Qwords + * are counted automatically. + * For more details, check description of dma_tag_t. + * @param packet2 Pointer to packet. + * @param next_tag Pointer to next tag. + * @param irq Interrupt Request. False by default. + * @param spr Memory/SPR Selection. False by default. + * @param pce Priority Control Enable. 0 by default. + */ + static inline void packet2_chain_call(packet2_t *packet2, const void *next_tag, u8 irq, u8 spr, u32 pce) + { + assert(((u32)packet2->next & 0xF) == 0); // Free space in packet is aligned properly. + packet2_chain_add_dma_tag(packet2, PACKET2_COUNT_QWC, pce, P2_DMA_TAG_CALL, irq, (u128 *)next_tag, spr); + } + + /** + * Add RET tag. + * NOTICE: packet2_chain_close_tag() required. Qwords + * are counted automatically. + * For more details, check description of dma_tag_t. + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + * @param pce Priority Control Enable. 0 by default. + */ + static inline void packet2_chain_ret(packet2_t *packet2, u8 irq, u32 pce) + { + packet2_chain_add_dma_tag(packet2, PACKET2_COUNT_QWC, pce, P2_DMA_TAG_RET, irq, 0, 0); + } + +#ifdef __cplusplus +} +#endif + +#endif /* __PACKET2_CHAIN_H__ */ diff --git a/ee/packet2/include/packet2_types.h b/ee/packet2/include/packet2_types.h new file mode 100644 index 00000000000..a5089cd790d --- /dev/null +++ b/ee/packet2/include/packet2_types.h @@ -0,0 +1,350 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** @file Structs and enums used in packet2 library. */ + +#ifndef __PACKET2_DATA_TYPES_H__ +#define __PACKET2_DATA_TYPES_H__ + +#include + +/** + * Two modes are commonly used with the DMA Controller (DMAC): normal mode and + * chain mode. In normal mode, transfers are issued one at a time. In chain mode, a + * series of transfers are issued simultaneously by initiating a transfer that contains + * "DMA Tags", which in turn contains instructions for further transfers. You will + * probably use chain mode for the majority of your transfers. + */ +enum Packet2Mode +{ + P2_MODE_NORMAL = 0, + P2_MODE_CHAIN = 1, +}; + +/** Types of memory mapping. */ +enum Packet2Type +{ + /** Normal. */ + P2_TYPE_NORMAL = 0x00000000, + /** Uncached. */ + P2_TYPE_UNCACHED = 0x20000000, + /** Uncached accelerated. */ + P2_TYPE_UNCACHED_ACCL = 0x30000000, + /** Scratchpad memory. */ + P2_TYPE_SPRAM = 0x70000000 +}; + +/** DMA tag types */ +enum DmaTagType +{ + /** Transfers the QWC qword from the ADDR field, clears the Dn_CHCR.STR to 0, and ends transfer. */ + P2_DMA_TAG_REFE = 0, + /** Transfers the QWC qword following the tag and reads the succeeding qword as the next tag. */ + P2_DMA_TAG_CNT = 1, + /** Transfers the QWC qword following the tag and reads the qword of the ADDR field as the next tag. */ + P2_DMA_TAG_NEXT = 2, + /** Transfers the QWC qword from the ADDR field and reads the qword following the tag as the next tag. */ + P2_DMA_TAG_REF = 3, + /** + * Transfers the QWC qword from the ADDR field while controlling stalls + * and reads the qword following the tag as the next tag. + * NOTICE: Effective only on the VIF1, GIF, and SIF1 channels. + */ + P2_DMA_TAG_REFS = 4, + /** + * Transfers the QWC qword following the tag, pushes the next field into + * the Dn_ASR register, and reads the qword of the ADDR field as the next tag. + * NOTICE: Effective only on the VIF0, VIF1, and GIF channels. + * Addresses can be pushed up to 2 levels + */ + P2_DMA_TAG_CALL = 5, + /** + * Transfers the QWC qword following the tag and reads the + * qword of the field popped from the Dn_ASR register as the + * next tag. + * Transfers the QWC qword following the tag, clears the + * Dn_CHCR.STR to 0, and ends transfer when there is no + * pushed address. + * NOTICE: Effective only on the VIF0, VIF1, and GIF channels. + */ + P2_DMA_TAG_RET = 6, + /** Transfers the QWC qword following the tag, clears the Dn_CHCR.STR to 0, and ends transfer. */ + P2_DMA_TAG_END = 7 +}; + +/** Destination chain tag. */ +typedef struct +{ + /** Quadword count. */ + u64 QWC : 16; + u64 PAD : 10; + /** + * Priority Control Enable. + * 00 Nothing performed + * 01 Reserved + * 10 Priority setting disabled (D_PCR.PCE = 0) + * 11 Priority setting enabled (D_PCR.PCE = 1) + */ + u64 PCE : 2; + /** Tag ID. (look at DmaTagType) */ + u64 ID : 3; + /** + * Interrupt Request. + * 0 No interrupt request + * 1 Interrupt request at end of packet transfer + */ + u64 IRQ : 1; + /** + * Address. + * Address of packet or next tag instruction + * (With qword alignment, lower 4 bits become 0.) + */ + u64 ADDR : 31; + /** + * Memory/SPR Selection. + * 0 Memory address + * 1 SPR address + */ + u64 SPR : 1; + u32 OPT1; + u32 OPT2; +} dma_tag_t __attribute__((aligned(16))); + +/** + * Unpack modes. + * V2 = 2 dimensions, S = Single = 1 dimension + * _32 = 32 bits of data length. + */ +enum UnpackMode +{ + P2_UNPACK_S_32 = 0x00, + P2_UNPACK_S_16 = 0x01, + P2_UNPACK_S_8 = 0x02, + P2_UNPACK_V2_32 = 0x04, + P2_UNPACK_V2_16 = 0x05, + P2_UNPACK_V2_8 = 0x06, + P2_UNPACK_V3_32 = 0x08, + P2_UNPACK_V3_16 = 0x09, + P2_UNPACK_V3_8 = 0x0A, + P2_UNPACK_V4_32 = 0x0C, + P2_UNPACK_V4_16 = 0x0D, + P2_UNPACK_V4_8 = 0x0E, + P2_UNPACK_V4_5 = 0x0F, +}; + +/** VIF opcodes. */ +enum VIFOpcode +{ + /** + * No operation. + * No operation is performed. + * NOP is used to adjust the data alignment in the VIF packet. + */ + P2_VIF_NOP = 0, + /** + * Sets write recycle. + * STCYCL writes the value of the IMMEDIATE field to the VIFn_CYCLE register. + */ + P2_VIF_STCYCL = 1, + /** + * Sets the double buffer offset. + * OFFSET writes the lower 10 bits of the IMMEDIATE field to the VIF1_OFST register. + * At the same time, the DBF flag of the VIF1_STAT register is cleared to 0, and the VIF1_BASE register + * value is set to the VIF1_TOPS. That is, the pointer for the double buffer points to the BASE. + */ + P2_VIF_OFFSET = 2, + /** + * Sets the base address of the double buffer. + * BASE writes the lower 10 bits of the IMMEDIATE field to the VIF1_BASE register. + * These bits become the base address of the double buffers. + */ + P2_VIF_BASE = 3, + /** + * Sets the data pointer. + * ITOP writes the lower 10 bits of the IMMEDIATE field to the VIFn_ITOPS register. + * This value is read from the VU by the XITOP instruction. + */ + P2_VIF_ITOP = 4, + /** + * Sets the addition decompression mode. + * STMOD writes the lower 2 bits of the IMMEDIATE field to the VIFn_MODE register. + * This becomes the addition decompression mode setting. + */ + P2_VIF_STMOD = 5, + /** + * Sets the PATH3 mask. + * MSKPATH3 enables/disables transfer processing via the GIF PATH3. + * The setting of this register applies to the next data block or later. + */ + P2_VIF_MSKPATH3 = 6, + /** + * Sets the MARK value. + * MARK writes the value of the IMMEDIATE field to the VIFn_MARK register. + * By properly setting the MARK value, it becomes possible to use this value for + * synchronization with the EE Core and debugging. + */ + P2_VIF_MARK = 7, + /** + * Waits for end of the microprogram. + * FLUSHE waits for the state in which the microprogram in VU0/VU1 has been ended. + */ + P2_VIF_FLUSHE = 16, + /** + * Waits for end of the microprogram. + * FLUSH waits for the state in which transfers to the GIF from PATH1 and PATH2 have been ended after + * end of microprogram in VU1. FLUSH does not wait for the end of the transfer via PATH1 by + * the XGKICK instruction with the E bit. + */ + P2_VIF_FLUSH = 17, + /** + * Waits for end of the microprogram. + * FLUSHA waits for the state in which there is no transfer request from PATH3 after the end of micro + * program in VU1 and end of transfer to the GIF from PATH1 and PATH2. + */ + P2_VIF_FLUSHA = 18, + /** + * Activates the microprogram. + * MSCAL waits for the end of the microprogram under execution and activates the micro program with the + * value of the IMMEDIATE field as the starting address. + */ + P2_VIF_MSCAL = 20, + /** + * Activates the microprogram. + * MSCNT waits for the end of the microprogram under execution and executes the next microprogram from + * the address following the most recently ended one in the PC (program counter). + */ + P2_VIF_MSCNT = 23, + /** + * Activates the microprogram. + * MSCALF waits for the end of both the microprogram and the GIF(PATH1/PATH2) transfer and executes + * the microprogram with the value of the IMMEDIATE field as the starting address. + */ + P2_VIF_MSCALF = 21, + /** + * Sets the data mask pattern. + * STMASK stores the next 1 word of data in the VIFn_MASK register. + * This data becomes the mask pattern of the write data. + */ + P2_VIF_STMASK = 32, + /** + * Sets the filling data. + * STROW stores the following 4 words of data in the VIFn_R0 - VIFn_R3 registers. + * These are used as filling data when decompressed by the VIFcode UNPACK. + */ + P2_VIF_STROW = 48, + /** + * Sets the filling data. + * STCOL stores the following 4 words of data in the VIFn_C0 - VIFn_C3 registers. + * These are used as filling data when decompressed by the VIFcode UNPACK. + */ + P2_VIF_STCOL = 49, + /** + * Transfers the microprogram. + * MPG waits for the end of the microprogram and transfers the following NUM pieces of 64-bit data. + * (microinstruction code) to the MicroMem address shown by the IMMEDIATE field. + */ + P2_VIF_MPG = 74, + /** + * Transfers data to the GIF(GS). + * DIRECT transfers the following IMMEDIATE pieces of 128-bit data to the GS via GIF PATH2. It is + * necessary to put the appropriate GIFtag to the data. + */ + P2_VIF_DIRECT = 80, + /** + * Transfers data to the GIF(GS). + * DIRECTHL transfers the following IMMEDIATE pieces of 128-bit data to the GS via GIF PATH2. + * The appropriate GIFtag must be attached to the data. + * Interruption of PATH3 IMAGE mode data does not occur during the data transfer via PATH2 by + * DIRECTHL. Moreover, when the IMAGE mode data is being transferred via PATH3, DIRECTHL stalls + * until the end of the transfer. + */ + P2_VIF_DIRECTHL = 81 +}; + +/** VIFCode structure. */ +typedef struct +{ + /** The contents of the IMMEDIATE field vary according to the CMD field. */ + u32 immediate : 16; + /** + * The NUM field shows the amount of data written to the VU Mem or MicroMem (Data is in 128-bit units + * and microinstruction code is in 64-bit units). When NUM=0, it is considered to be 256. + * Note that it is not the amount of data in the VIF packet. NUM pieces of data can be written even when the + * amount of data in the packet is 0 depending on the CYCLE register setting. + */ + u32 num : 8; + /** + * The CMD field directs the operation of the VIF, that is, the decompression + * method of the following data. + */ + u32 cmd : 8; +} vif_code_t; + +/** + * DMA data packet. + * Successor of standard packet. + */ +typedef struct +{ + /** Maximum number of qwords which was defined in packet2_create(). */ + u16 max_qwords_count; + /** Type of packet's memory mapping. */ + enum Packet2Type type; + /** Packet mode. */ + enum Packet2Mode mode; + /** + * Tag transfer enable. + * If >0 transfer tag is set during packet sending. + * Effective only in chain mode. + */ + u8 tte; + /** Start position of the buffer. */ + qword_t *base __attribute__((aligned(64))); + /** Current position of the buffer. */ + qword_t *next; + /** + * The buffer position where DMA tag was opened. + * NULL, if no dma tag is open. + */ + dma_tag_t *tag_opened_at; + /** + * The buffer position where DIRECT/UNPACK was opened. + * NULL, if no DIRECT/UNPACK is open. + */ + vif_code_t *vif_code_opened_at; + /** + * Helper variable to control bytes that + * was already "written" to VU. + */ + u32 vif_added_bytes; +} packet2_t; + +/** Mask, used in VIF's STMASK opcode. */ +typedef struct +{ + unsigned int m0 : 2; + unsigned int m1 : 2; + unsigned int m2 : 2; + unsigned int m3 : 2; + unsigned int m4 : 2; + unsigned int m5 : 2; + unsigned int m6 : 2; + unsigned int m7 : 2; + unsigned int m8 : 2; + unsigned int m9 : 2; + unsigned int m10 : 2; + unsigned int m11 : 2; + unsigned int m12 : 2; + unsigned int m13 : 2; + unsigned int m14 : 2; + unsigned int m15 : 2; +} Mask; + +#endif /* __PACKET2_DATA_TYPES_H__ */ diff --git a/ee/packet2/include/packet2_utils.h b/ee/packet2/include/packet2_utils.h new file mode 100644 index 00000000000..7fc6af76437 --- /dev/null +++ b/ee/packet2/include/packet2_utils.h @@ -0,0 +1,252 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** @file Helper functions for packet2. */ + +#ifndef __PACKET2_UTILS_H__ +#define __PACKET2_UTILS_H__ + +#include "packet2.h" +#include +#include +#include +#include +#include + +#define VU_GS_PRIM(PRIM, IIP, TME, FGE, ABE, AA1, FST, CTXT, FIX) (u128)(((FIX << 10) | (CTXT << 9) | (FST << 8) | (AA1 << 7) | (ABE << 6) | (FGE << 5) | (TME << 4) | (IIP << 3) | (PRIM))) +#define VU_GS_GIFTAG(NLOOP, EOP, PRE, PRIM, FLG, NREG) (((u64)(NREG) << 60) | ((u64)(FLG) << 58) | ((u64)(PRIM) << 47) | ((u64)(PRE) << 46) | (EOP << 15) | (NLOOP << 0)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + // ---- + // VU + // ---- + + /** + * Add VU buffers size settings. + * @param t_base Base address (qw) + * @param t_offset Offset address (qw) + */ + static inline void packet2_utils_vu_add_double_buffer(packet2_t *packet2, u16 base, u16 offset) + { + packet2_chain_open_cnt(packet2, 0, 0, 0); + packet2_vif_base(packet2, base, 0); + packet2_vif_offset(packet2, offset, 0); + packet2_chain_close_tag(packet2); + } + + /** + * Add data via REF tag and UNPACK. + * Please be sure that data is aligned, + * and there is no any open UNPACK/DIRECT. + * @param packet2 Pointer to packet2. + * @param t_dest_address Destination address. + * @param t_data Data pointer. + * @param t_size Size in quadwords. + * @param t_use_top Unpack to current double buffer? + * When true, data will be loaded at destination address + beginning of current VU buffer. + */ + static inline void packet2_utils_vu_add_unpack_data(packet2_t *packet2, u32 t_dest_address, void *t_data, u32 t_size, u8 t_use_top) + { + packet2_chain_ref(packet2, t_data, t_size, 0, 0, 0); + packet2_vif_stcycl(packet2, 0, 0x0101, 0); + packet2_vif_open_unpack(packet2, P2_UNPACK_V4_32, t_dest_address, t_use_top, 0, 1, 0); + packet2_vif_close_unpack(packet2, t_size); + packet2->vif_added_bytes += t_size << 4; + } + + /** + * Open CNT tag + VU unpack. + * @param packet2 Pointer to packet2. + * @param t_dest_address Destination address. + * @param t_use_top Unpack to current double buffer? + */ + static inline void packet2_utils_vu_open_unpack(packet2_t *packet2, u32 t_dest_address, u8 t_use_top) + { + packet2_chain_open_cnt(packet2, 0, 0, 0); + packet2_vif_stcycl(packet2, 0, 0x0101, 0); + packet2_vif_open_unpack(packet2, P2_UNPACK_V4_32, t_dest_address, t_use_top, 0, 1, 0); + } + + /** Close CNT tag + VU unpack. */ + static inline void packet2_utils_vu_close_unpack(packet2_t *packet2) + { + packet2_align_to_qword(packet2); + packet2_chain_close_tag(packet2); + packet2_vif_close_unpack(packet2, packet2_get_vif_added_qws(packet2)); + } + + /** + * Continue VU micro program (from --cont line). + * Adds FLUSH and MSCNT VIF opcodes. + */ + static inline void packet2_utils_vu_add_continue_program(packet2_t *packet2) + { + packet2_chain_open_cnt(packet2, 0, 0, 0); + packet2_vif_flush(packet2, 0); + packet2_vif_mscnt(packet2, 0); + packet2_chain_close_tag(packet2); + } + + /** + * Start VU micro program. + * Adds FLUSH and MSCAL VIF opcodes. + */ + static inline void packet2_utils_vu_add_start_program(packet2_t *packet2, u32 addr) + { + packet2_chain_open_cnt(packet2, 0, 0, 0); + packet2_vif_flush(packet2, 0); + packet2_vif_mscal(packet2, addr, 0); + packet2_chain_close_tag(packet2); + } + + /** Add aligned END tag (with nops). */ + static inline void packet2_utils_vu_add_end_tag(packet2_t *packet2) + { + packet2_chain_open_end(packet2, 0, 0); + packet2_vif_nop(packet2, 0); + packet2_vif_nop(packet2, 0); + packet2_chain_close_tag(packet2); + } + + /** + * Get program size in qws + * @param start Start address. + * @param end End address. + * @returns Packet size in qws for specified program (instructions/256 + 1qw for tolerance). + */ + static inline u32 packet2_utils_get_packet_size_for_program(u32 *start, u32 *end) + { + // Count instructions + u32 count = (end - start) / 2; + if (count & 1) + count++; + return (count >> 8) + 1; + } + + // ---- + // GIF + // ---- + + /** + * Add set GIFTag which will be + * sent from VU1 via XGKICK instruction. + * @param packet2 Pointer to packet2. + * @param loops_count How many GIF tags there will be? + */ + static inline void packet2_utils_gif_add_set(packet2_t *packet2, u32 loops_count) + { + packet2_add_2x_s64(packet2, GIF_SET_TAG(loops_count, 0, 0, 0, GIF_FLG_PACKED, 1), GIF_REG_AD); + } + + // ---- + // GS + // ---- + + /** + * Add lod GIFTag which will be + * sent from VU1 via XGKICK instruction. + * @param packet2 Pointer to packet2. + * @param lod Pointer to lod settings. + */ + static inline void packet2_utils_gs_add_lod(packet2_t *packet2, lod_t *lod) + { + packet2_add_2x_s64( + packet2, + GS_SET_TEX1( + lod->calculation, + lod->max_level, + lod->mag_filter, + lod->min_filter, + lod->mipmap_select, + lod->l, + (int)(lod->k * 16.0F)), + GS_REG_TEX1); + } + + /** + * Add texture buffer / clut GIFTag which will be + * sent from VU1 via XGKICK instruction. + * @param packet2 Pointer to packet2. + * @param texbuff Pointer to texture buffer. + * @param clut Pointer to clut buffer. + */ + static inline void packet2_utils_gs_add_texbuff_clut(packet2_t *packet2, texbuffer_t *texbuff, clutbuffer_t *clut) + { + packet2_add_2x_s64( + packet2, + GS_SET_TEX0( + texbuff->address >> 6, + texbuff->width >> 6, + texbuff->psm, + texbuff->info.width, + texbuff->info.height, + texbuff->info.components, + texbuff->info.function, + clut->address >> 6, + clut->psm, + clut->storage_mode, + clut->start, + clut->load_method), + GS_REG_TEX0); + } + + /** + * Add draw finish event GIFTag which will be sent from VU1 via + * XGKICK instruction. + * Used for synchronization via draw_wait_finish() + * @param packet2 Pointer to packet2. + */ + static inline void packet2_utils_gs_add_draw_finish_giftag(packet2_t *packet2) + { + packet2_add_2x_s64(packet2, 1, GS_REG_FINISH); + } + + /** + * Add drawing (prim) GIFTag which will be sent from VU1 via + * XGKICK instruction. + * @param packet2 Pointer to packet2. + * @param prim Pointer to prim settings. + * @param loops_count GIF tag loops count. + * @param nreg What types of data we will use? + * @param nreg_count How many types there are in nreg? + * @param context Drawing context + */ + static inline void packet2_utils_gs_add_prim_giftag(packet2_t *packet2, prim_t *prim, u32 loops_count, u32 nreg, u8 nreg_count, u8 context) + { + packet2_add_2x_s64( + packet2, + VU_GS_GIFTAG( + loops_count, // Information for GS. Amount of loops + 1, + 1, + VU_GS_PRIM( + prim->type, + prim->shading, + prim->mapping, + prim->fogging, + prim->blending, + prim->antialiasing, + prim->mapping_type, + context, // context + prim->colorfix), + 0, + nreg_count), + nreg); + } + +#ifdef __cplusplus +} +#endif + +#endif /* __PACKET2_UTILS_H__ */ diff --git a/ee/packet2/include/packet2_vif.h b/ee/packet2/include/packet2_vif.h new file mode 100644 index 00000000000..ba60eb5a517 --- /dev/null +++ b/ee/packet2/include/packet2_vif.h @@ -0,0 +1,364 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** @file VIF related functions for packet2. */ + +#ifndef __PACKET2_VIF_H__ +#define __PACKET2_VIF_H__ + +#include +#include + +#define MAKE_VIF_CODE(_immediate, _num, _cmd, _irq) ((u32)(_immediate) | ((u32)(_num) << 16) | ((u32)(_cmd) << 24) | ((u32)(_irq) << 31)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Add UNPACK VIF opcode. + * NOTICE: packet2_vif_close_unpack() required. Qwords + * are counted automatically. + * For more details, check description of vif_code_t. + * @param packet2 Pointer to packet. + * @param mode Unpack mode + * @param vuAddr Memory address (divided by 16). + * @param dblBuffered (VIF1 only) + * 1 - Adds VIF1_TOPS register to ADDR + * 0 - Does not use VIF1_TOPS register. + * @param masked Is masked + * @param usigned + * 1 - Unsigned - Decompress by padding 0 to the upper field + * 0 - Signed - Decompress by sign extension + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_open_unpack(packet2_t *packet2, enum UnpackMode mode, u32 vuAddr, u8 dblBuffered, u8 masked, u8 usigned, u8 irq) + { + assert(packet2->vif_code_opened_at == NULL); // All previous UNPACK/DIRECT are closed. + packet2->vif_code_opened_at = (vif_code_t *)packet2->next; + *((u32 *)packet2->next)++ = + MAKE_VIF_CODE(vuAddr | ((u32)usigned << 14) | ((u32)dblBuffered << 15), + 0, + mode | ((u32)masked << 4) | 0x60, irq); + } + + /** + * Close UNPACK automatically. + * In reality, get back to pointer "vif_code_opened_at" and + * fix immediate value with qwords counted from last packet2_vif_open_direct(). + * @param packet2 Pointer to packet. + * @param unpack_num Amount of data written to the VU Mem (qwords) or MicroMem (dwords). + * 256 is max value! + */ + static inline void packet2_vif_close_unpack(packet2_t *packet2, u16 unpack_num) + { + assert(packet2->vif_code_opened_at != NULL); // There is open UNPACK/DIRECT. + assert(((u32)packet2->next & 0x3) == 0); // Make sure we're u32 aligned + assert((packet2->vif_code_opened_at->cmd & 0x60) == 0x60); // It was UNPACK + packet2->vif_code_opened_at->num = (unpack_num == 256) ? 0 : unpack_num; + packet2->vif_code_opened_at = (vif_code_t *)NULL; + } + + /** + * Add DIRECT VIF opcode. + * NOTICE: Direct must be closed via + * packet2_vif_close_direct_manual() or + * packet2_vif_close_direct_auto() function! + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_open_direct(packet2_t *packet2, u8 irq) + { + assert(packet2->vif_code_opened_at == NULL); // All previous UNPACK/DIRECT are closed. + packet2->vif_code_opened_at = (vif_code_t *)packet2->next; + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_DIRECT, irq); + } + + /** + * Close DIRECT manually. + * In reality, get back to pointer "vif_code_opened_at" and + * fix immediate value with given value. + * @param packet2 Pointer to packet. + * @param qwords Qwords count. + */ + static inline void packet2_vif_close_direct_manual(packet2_t *packet2, u32 qwords) + { + assert(packet2->vif_code_opened_at != NULL); // There is open UNPACK/DIRECT. + assert((((u32)packet2->next - ((u32)packet2->vif_code_opened_at + 4)) & 0xF) == 0); + packet2->vif_code_opened_at->immediate = qwords; + packet2->vif_code_opened_at = (vif_code_t *)NULL; + } + + /** + * Close DIRECT automatically. + * In reality, get back to pointer "vif_code_opened_at" and + * fix immediate value with qwords counted from last packet2_vif_open_direct(). + * @param packet2 Pointer to packet. + */ + static inline void packet2_vif_close_direct_auto(packet2_t *packet2) + { + return packet2_vif_close_direct_manual(packet2, ((u32)packet2->next - ((u32)packet2->vif_code_opened_at + 4)) >> 4); + } + + /** + * Add NOP VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_nop(packet2_t *packet2, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_NOP, irq); + } + + /** + * Add MPG VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param num Number of 64-bit data. + * @param addr Address. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_mpg(packet2_t *packet2, u32 num, u32 addr, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(addr, num, P2_VIF_MPG, irq); + } + + /** + * Add STCYCL VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param wl WL field. + * @param cl CL field. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_stcycl(packet2_t *packet2, u32 wl, u32 cl, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(cl | (wl << 8), 0, P2_VIF_STCYCL, irq); + } + + /** + * Add OFFSET VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param offset Offset. BASE+OFFSET will be the address of second buffer. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_offset(packet2_t *packet2, u32 offset, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(offset, 0, P2_VIF_OFFSET, irq); + } + + /** + * Add BASE VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param base Base address of double buffer. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_base(packet2_t *packet2, u32 base, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(base, 0, P2_VIF_BASE, irq); + } + + /** + * Add FLUSH VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_flush(packet2_t *packet2, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_FLUSH, irq); + } + + /** + * Add MSCAL VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param addr Starting address. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_mscal(packet2_t *packet2, u32 addr, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(addr, 0, P2_VIF_MSCAL, irq); + } + + /** + * Add MSCNT VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_mscnt(packet2_t *packet2, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_MSCNT, irq); + } + + /** + * Add ITOP VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param itops Value for VU XITOP instruction. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_itop(packet2_t *packet2, u32 itops, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(itops, 0, P2_VIF_ITOP, irq); + } + + /** + * Add MSKPATH3 VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param mode Decompression mode. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_stmod(packet2_t *packet2, u32 mode, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(mode, 0, P2_VIF_STMOD, irq); + } + + /** + * Add MSKPATH3 VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param mask Mask. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_mskpath3(packet2_t *packet2, u32 mask, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(mask, 0, P2_VIF_MSKPATH3, irq); + } + + /** + * Add MARK VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param value Value. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_mark(packet2_t *packet2, u32 value, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(value, 0, P2_VIF_MARK, irq); + } + + /** + * Add FLUSHE VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_flushe(packet2_t *packet2, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_FLUSHE, irq); + } + + /** + * Add FLUSHA VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_flusha(packet2_t *packet2, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_FLUSHA, irq); + } + + /** + * Add MSCALF VIF opcode. + * NOTICE: Alignment alert. Size of word (1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param addr Address. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_mscalf(packet2_t *packet2, u32 addr, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(addr, 0, P2_VIF_MSCALF, irq); + } + + /** + * Add STMASK VIF opcode. + * NOTICE: Alignment alert. Size of dword (1/2). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param mask Mask. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_stmask(packet2_t *packet2, Mask mask, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_STMASK, irq); + *((Mask *)packet2->next)++ = mask; + } + + /** + * Add STROW VIF opcode. + * NOTICE: Alignment alert. Size of 5 x word (1+1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param row_arr Row array. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_strow(packet2_t *packet2, const void *row_arr, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_STROW, irq); + *((u32 *)packet2->next)++ = ((u32 *)row_arr)[0]; + *((u32 *)packet2->next)++ = ((u32 *)row_arr)[1]; + *((u32 *)packet2->next)++ = ((u32 *)row_arr)[2]; + *((u32 *)packet2->next)++ = ((u32 *)row_arr)[3]; + } + + /** + * Add STCOL VIF opcode. + * NOTICE: Alignment alert. Size of 5 x word (1+1/4). + * For more details, check description of VIFOpcode. + * @param packet2 Pointer to packet. + * @param col_arr Column array. + * @param irq Interrupt Request. False by default. + */ + static inline void packet2_vif_stcol(packet2_t *packet2, const void *col_arr, u8 irq) + { + *((u32 *)packet2->next)++ = MAKE_VIF_CODE(0, 0, P2_VIF_STCOL, irq); + *((u32 *)packet2->next)++ = ((u32 *)col_arr)[0]; + *((u32 *)packet2->next)++ = ((u32 *)col_arr)[1]; + *((u32 *)packet2->next)++ = ((u32 *)col_arr)[2]; + *((u32 *)packet2->next)++ = ((u32 *)col_arr)[3]; + } + + /** + * Add VU micro program into packet2. + * Packet2 MODE for micro program upload: Chain + * @param dest VU destination address (divided by 16). + * @param start Start address. + * @param end End address. + */ + void packet2_vif_add_micro_program(packet2_t *packet2, u32 dest, u32 *start, u32 *end); + +#ifdef __cplusplus +} +#endif + +#endif /* __PACKET2_VIF_H__ */ diff --git a/ee/packet2/src/erl-support.c b/ee/packet2/src/erl-support.c new file mode 100644 index 00000000000..46278a79f0c --- /dev/null +++ b/ee/packet2/src/erl-support.c @@ -0,0 +1,18 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# The erl-tags support +*/ + +#include + +char *erl_id = "libpacket2"; +char *erl_dependancies[] = { + "libc", + 0}; diff --git a/ee/packet2/src/packet2.c b/ee/packet2/src/packet2.c new file mode 100644 index 00000000000..9d753fa183f --- /dev/null +++ b/ee/packet2/src/packet2.c @@ -0,0 +1,133 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +#include +#include +#include +#include +#include +#include + +#define P2_ALIGNMENT 64 +#define P2_MAKE_PTR_NORMAL(PTR) ((u32)(PTR)&0x0FFFFFFF) + +// --- +// Packet2 management +// --- + +packet2_t *packet2_create(u16 qwords, enum Packet2Type type, enum Packet2Mode mode, u8 tte) +{ + packet2_t *packet2 = (packet2_t *)calloc(1, sizeof(packet2_t)); + if (packet2 == NULL) + return NULL; + + packet2->max_qwords_count = qwords; + packet2->type = type; + packet2->mode = mode; + packet2->tte = tte; + packet2->vif_added_bytes = 0; + packet2->tag_opened_at = NULL; + packet2->vif_code_opened_at = NULL; + + // Dma buffer size should be a whole number of cache lines (64 bytes = 4 quads) + assert(!((packet2->type == P2_TYPE_UNCACHED || packet2->type == P2_TYPE_UNCACHED_ACCL) && packet2->max_qwords_count & (4 - 1))); + + u32 byte_size = packet2->max_qwords_count << 4; + + if ((packet2->base = memalign(P2_ALIGNMENT, byte_size)) == NULL) + { + free(packet2); + return NULL; + } + + packet2->base = packet2->next = (qword_t *)((u32)packet2->base | packet2->type); + + memset(packet2->base, 0, byte_size); + + // "I hate to do this, but I've wasted FAR too much time hunting down cache incoherency" + if (packet2->type == P2_TYPE_UNCACHED || packet2->type == P2_TYPE_UNCACHED_ACCL) + FlushCache(0); + + return packet2; +} + +packet2_t *packet2_create_from(qword_t *base, qword_t *next, u16 qwords, enum Packet2Type type, enum Packet2Mode mode, u8 tte) +{ + // Dma buffer size should be a whole number of cache lines (64 bytes = 4 quads) + assert(!((type == P2_TYPE_UNCACHED || type == P2_TYPE_UNCACHED_ACCL) && qwords & (4 - 1))); + + // Dma buffer should be aligned on a cache line (64-byte boundary) + assert(!((type == P2_TYPE_UNCACHED || type == P2_TYPE_UNCACHED_ACCL) && (u32)base & (64 - 1))); + + packet2_t *packet2 = (packet2_t *)calloc(1, sizeof(packet2_t)); + if (packet2 == NULL) + return NULL; + + packet2->base = base; + packet2->next = next; + packet2->max_qwords_count = qwords; + packet2->type = type; + packet2->tte = tte; + packet2->mode = mode; + return packet2; +} + +void packet2_free(packet2_t *packet2) +{ + if (packet2->base != NULL) + free((qword_t *)P2_MAKE_PTR_NORMAL(packet2->base)); + free(packet2); +} + +void packet2_reset(packet2_t *packet2, u8 clear_mem) +{ + packet2->next = packet2->base; + packet2->vif_code_opened_at = NULL; + packet2->tag_opened_at = NULL; + packet2->vif_added_bytes = 0; + if (clear_mem) + memset(packet2->base, 0, packet2->max_qwords_count << 4); +} + +// --- +// Utils +// --- + +void packet2_add(packet2_t *a, packet2_t *b) +{ + assert(packet2_get_qw_count(a) + packet2_get_qw_count(b) <= a->max_qwords_count); + memcpy(a->next, b->base, (u32)b->next - (u32)b->base); + a->next = a->base + packet2_get_qw_count(b) + 1; + a->vif_added_bytes += b->vif_added_bytes; +} + +void packet2_print(packet2_t *packet2, u32 qw_count) +{ + if (qw_count == 0) + qw_count = ((u32)packet2->next - (u32)packet2->base) >> 4; + printf("\n============================\n"); + printf("Packet2: Dumping %d words...\n", ((u32)packet2->next - (u32)packet2->base) >> 2); + u32 i = 0; + u32 *nextWord; + for (nextWord = (u32 *)packet2->base; nextWord != (u32 *)packet2->next; nextWord++, i++) + { + if ((i % 4) == 0) + printf("\n0x%08x: ", (u32)nextWord); + printf("0x%08x ", *nextWord); + if (i / 4 == qw_count) + break; + } + printf("\n============================\n"); +} + +void packet2_print_qw_count(packet2_t *packet2) +{ + printf("Packet2: Qwords count:%d \n", packet2_get_qw_count(packet2)); +} diff --git a/ee/packet2/src/packet2_vif.c b/ee/packet2/src/packet2_vif.c new file mode 100644 index 00000000000..625f1c612ee --- /dev/null +++ b/ee/packet2/src/packet2_vif.c @@ -0,0 +1,32 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# (c) 2020 h4570 Sandro Sobczyński +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +#include "packet2_vif.h" +#include "packet2_chain.h" + +void packet2_vif_add_micro_program(packet2_t *packet2, u32 dest, u32 *start, u32 *end) +{ + // get the size of the code as we can only send 256 instructions in each MPGtag + u32 count = (end - start) / 2; + if (count & 1) + count++; + + u32 *l_start = start; + while (count > 0) + { + u16 curr_count = count > 256 ? 256 : count; + packet2_chain_ref(packet2, l_start, curr_count / 2, 0, 0, 0); + packet2_vif_nop(packet2, 0); + packet2_vif_mpg(packet2, curr_count & 0xFF, dest, 0); + l_start += curr_count * 2; + count -= curr_count; + dest += curr_count; + } +}