Leaderboard
Popular Content
Showing content with the highest reputation on 12/04/2017 in Posts
-
I haven't told much about the latest games I've picked up recently. So here they are: I recently got a PS3 since my old one died years ago and I was finally able to start playing this gem again on it. I also picked up these games! And lastly but definitely not least! My 64Drive arrived and have been enjoying it thoroughly so far! I've gotten to test out even a few of my old hacks to see if they'd run on hardware and they do! Pix below!1 point
-
I was watching one of the Mario 64 videos that explained that and it even showed what happens when the number gets too big. Causes it to round and make Mario skip huge segments of mesh while walking and incrementing his x or z axis. It can get pretty crazy.1 point
-
The Matrix Format This is confusing as hell to explain, so I think it's best to start by explaining how 32-bit fixed-point calculations work. A 32-bit fixed-point value has the format 's15.16', where the 's' represents the sign-bit, the 15 represents the 15 integer bits, and the 16 represents the 16 fractional bits. For the fractional part, a value of 0 is equal to 0, and a value of 65,536 is equal to 1. Note that you can never actually have a fractional value of 1, because the carried bit exceeds the 16-bit range and the value wraps back around to 0. You can convert to this format simply by multiplying by 65,536. eg. 0.5 * 65,536 = 32,768 = 0x8000. You can also convert back by dividing by 65,536. It's important to note that the carried bit that was mentioned before will actually overflow into the integer part of the number; just like how 0.9 + 0.2 = 1.1. In addition to the encoding of the fractional number information, the s15.16 format also uses two's complement encoding. This means that when the sign-bit is set, the actual integer value is 0 - (32,768 - the_value). eg. a value of 32,765 with the sign-bit set (0xFFFD) would equal 0 - (32,768 - 32,765) = -3. The same rule also applies to the fractional value, where the actual fractional value is 0 - (1.0 - the_value) when the sign-bit is set. That said, this is just a side effect of the two's complement encoding, and has nothing to do particularly with the fractional number format. The last thing we need to cover before we can get on to the actual matrix format is how overflow from addition and multiplication is handled for fixed-point values. Well, first things first, because of the way that the fractional component is encoded, fixed-point addition, subtraction, multiplication, and division are all equivalent to integer addition, subtraction, multiplication, and division. This makes things much easier. The second thing you need to know is that the overflow from 32-bit addition and multiplication can easily be captured just by using 64-bit arithmetic; the confusing part is converting back to a 32-bit value. So how does the N64 do it? It extracts the "middle" 32-bits of the 64-bit value. This seems like some sort of sorcery, but just try it: 1.5 = (1 << 16) + (0.5 * 65536) = 0x00018000 0x00018000 * 0x00018000 = 0x0000000240000000 0x0000000240000000 -> 0x0000 00024000 0000 -> 0x00024000 0x00024000 >> 16 = 0x0002 = 2 0x00024000 & 0xFFFF = 0x4000 = 16384 16384 / 65536 = 0.25 2 + 0.25 = 2.25 1.5 * 1.5 = 2.25 Crazy, huh? That said, some of the values in rotation and projection matrices have really small fractional values prone to generate errors when multiplied against factors with much higher values. As a result, the N64 manual states that developers should perform their actual calculations using floating-point and only convert the matrices to fixed-point when they're uploading to the RSP. Speaking of which, now that we understand how the math for each individual value works, it's time to look at the matrix format itself. struct Matrix { int16_t integer_parts[4][4]; uint16_t fractional_parts[4][4]; }; Well, that makes no fucking sense, but that's what it is. If you're unfamiliar with C syntax, a [4][4] array is equivalent to a [16] element array. Technically speaking, the gbi.h defines the matrix as a 'long [4][4]', which requires bit shifting integers together and fractionals together (which I personally find hideous and confusing).1 point
-
I wasn't going to post this because I figured it'd be a waste of time, but I figured, why not. The Zelda64 Overlay Format The overlays in Zelda64 games are fairly simple object files. It's just a dump of the ".text" section, followed by a dump of the ".data" section, and then a dump of the ".rodata" section. Immediately after this, there's a small structure with the format: struct { uint32_t size_of_text_section_in_bytes; uint32_t size_of_data_section_in_bytes; uint32_t size_of_rodata_section_in_bytes; uint32_t size_of_bss_section_in_bytes; uint32_t number_of_relocations; }; This is just common knowledge to people who are familiar with object files, but the ".text" section contains the code, the ".data" section contains data that's both readable and writable, the ".rodata" section contains data that's read-only, and the ".bss" section contains data that's initialized to 0 (hence the reason that there is no dump of the ".bss" section in the overlay file. Immediately after this structure is the relocations themselves. Each relocation is a 32-bit value. The uppermost 4-bits is the section type: enum { TEXT_SECTION = 0, DATA_SECTION = 1, RODATA_SECTION = 2, BSS_SECTION = 3 }; The next 4-bits is the relocation type. Each relocation type corresponds exactly to the MIPS relocation types of the ELF file format. As you can see, relocations are actually pretty simple to do. You just extract the operands, apply the calculation, and write the result back. That said, there are a lot of different relocation types, so if you want to fully support the linking and/or loading of object files, it's going to be a bit of a pain in the ass. Immediately after all of the relocations, padding is added to the file such that it's 16-byte aligned minus 4-bytes; and there must be more than 0 bytes of padding. So if the file ends with: 00001000: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx 00001010: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx 00001020: xx xx xx xx xx xx xx xx xx xx xx xx Then you'll need to add 16 bytes of padding. Immediately after this padding is a 32-bit pointer to the structure that contains the section sizes and the number of relocations. The Combiner Modes The combiner is executed after the texturizing unit samples and filters texels from the tiles (which is also after the rasterizer generates fragments from the vertices). There are 2 types of combining: color combining, and alpha combining. Both use the equation "(A - B ) * C + D", but the operands that they accept differ. For the color combiner: enum { CC_A_COMBINED_COLOR = 0, // the combined color from cycle 1 CC_A_TEXEL0_COLOR = 1, // the color of the texel for tile 0 CC_A_TEXEL1_COLOR = 2, // the color of the texel for tile 1 CC_A_PRIMITIVE_COLOR = 3, // the primitive color, set via the 0xFA SetPrimColor command CC_A_SHADE_COLOR = 4, // the shade (vertex) color CC_A_ENVIRONMENT_COLOR = 5, // the environment color, set via the 0xFB SetEnvColor command CC_A_1 = 6, // vec3 { 1.0, 1.0, 1.0 } rgb CC_A_NOISE = 7, // a random color CC_A_0 = 8 // vec3 { 0.0, 0.0, 0.0 } rgb ... // values 9..15 are all the same as value 8 }; enum { CC_B_COMBINED_COLOR = 0, // the combined color from cycle 1 CC_B_TEXEL0_COLOR = 1, // the color of the texel for tile 0 CC_B_TEXEL1_COLOR = 2, // the color of the texel for tile 1 CC_B_PRIMITIVE_COLOR = 3, // the primitive color, set via the 0xFA SetPrimColor command CC_B_SHADE_COLOR = 4, // the shade (vertex) color CC_B_ENVIRONMENT_COLOR = 5, // the environment color, set via the 0xFB SetEnvColor command CC_B_CENTER = 6, // the chroma key center, set via the SetKeyR and SetKeyGB commands CC_B_K4 = 7, // the chroma key K4 constant, set via the SetConvert command CC_B_0 = 8 // vec3 { 0.0, 0.0, 0.0 } rgb ... // values 9..15 are all the same as value 8 }; enum { CC_C_COMBINED_COLOR = 0, // the combined color from cycle 1 CC_C_TEXEL0_COLOR = 1, // the color of the texel for tile 0 CC_C_TEXEL1_COLOR = 2, // the color of the texel for tile 1 CC_C_PRIMITIVE_COLOR = 3, // the primitive color, set via the 0xFA SetPrimColor command CC_C_SHADE_COLOR = 4, // the shade (vertex) color CC_C_ENVIRONMENT_COLOR = 5, // the environment color, set via the 0xFB SetEnvColor command CC_C_SCALE = 6, // the chroma key scale, set via the SetKeyR and SetKeyGB commands CC_C_COMBINED_ALPHA = 7, // the combined alpha from cycle 1 CC_C_TEXEL0_ALPHA = 8, // the alpha of the texel for tile 0 CC_C_TEXEL1_ALPHA = 9, // the alpha of the texel for tile 1 CC_C_PRIMITIVE_ALPHA = 10, // the primitive alpha, set via the 0xFA SetPrimColor command CC_C_SHADE_ALPHA = 11, // the shade (vertex) alpha CC_C_ENVIRONMENT_ALPHA = 12, // the environment alpha, set via the 0xFB SetEnvColor command CC_C_LOD = 13, // the actual lod fraction CC_C_PRIMITIVE_LOD = 14, // the primitive lod fraction, set via the 0xFA SetPrimColor command CC_C_K5 = 15, // the chroma key K5 constant, set via the SetConvert command CC_C_0 = 16, // vec3 { 0.0, 0.0, 0.0 } rgb ... // values 17..31 are all the same as value 16 }; enum { CC_D_COMBINED_COLOR = 0, // the combined color from cycle 1 CC_D_TEXEL0_COLOR = 1, // the color of the texel for tile 0 CC_D_TEXEL1_COLOR = 2, // the color of the texel for tile 1 CC_D_PRIMITIVE_COLOR = 3, // the primitive color, set via the 0xFA SetPrimColor command CC_D_SHADE_COLOR = 4, // the shade (vertex) color CC_D_ENVIRONMENT_COLOR = 5, // the environment color, set via the 0xFB SetEnvColor command CC_D_1 = 6, // vec3 { 1.0, 1.0, 1.0 } rgb CC_D_0 = 7 // vec3 { 0.0, 0.0, 0.0 } rgb }; Well, that seems messy as fuck, but there's no helping that; just a side effect of them trying to cram as much crap into a single command as possible. Anyhow, here's the alpha combiner modes: enum { AC_ABD_COMBINED_ALPHA = 0, // the combined alpha from cycle 1 AC_ABD_TEXEL0_ALPHA = 1, // the alpha of the texel for tile 0 AC_ABD_TEXEL1_ALPHA = 2, // the alpha of the texel for tile 1 AC_ABD_PRIMITIVE_ALPHA = 3, // the primitive alpha, set via the 0xFA SetPrimColor command AC_ABD_SHADE_ALPHA = 4, // the shade (vertex) alpha AC_ABD_ENVIRONMENT_ALPHA = 5, // the environment alpha, set via the 0xFB SetEnvColor command AC_ABD_1 = 6, // 1.0 AC_ABD_0 = 7, // 0.0 } enum { AC_C_LOD = 0, // the lod fraction AC_C_TEXEL0_ALPHA = 1, // the alpha of the texel for tile 0 AC_C_TEXEL1_ALPHA = 2, // the alpha of the texel for tile 1 AC_C_PRIMITIVE_ALPHA = 3, // the primitive alpha, set via the 0xFA SetPrimColor command AC_C_SHADE_ALPHA = 4, // the shade (vertex) alpha AC_C_ENVIRONMENT_ALPHA = 5, // the environment alpha, set via the 0xFB SetEnvColor command AC_C_PRIMITIVE_LOD = 6, // the primitive lod fraction, set via the 0xFA SetPrimColor command AC_C_0 = 7, // 0.0 }; One thing that should also be mentioned (if you haven't inferred it already from the comments above, is that the 0xFC SetCombine command actually sets 2 color combine modes and 2 alpha combine modes at the same time. In single-cycle mode, the 2 modes are exactly the same, and the 'COMBINED_COLOR / COMBINED_ALPHA' values are unused. In double-cycle mode, the two modes are different. Double-cycle mode is typically used for multitexturing. Algebraically speaking, because the combining equation is (A - B ) * C + D; the following statements hold true: Rule 1: if B = 0, then (A * C + D) = ((A - * C + D) Rule 2: if C = 0, then (D) = ((A - * C + D) Rule 3: if D = 0, then ((A - * C) = ((A - * C + D) Rule 4: if B = 0 and D = 0, then (A * C) = ((A - * C + D) Given these statements, here are some common color combiner modes: Modulate = TEXEL0_COLOR * SHADE_COLOR // See Rule 4 Decal = TEXEL0_COLOR // See Rule 2 Blend = (PRIMITIVE_COLOR - ENVIRONMENT_COLOR) * TEXEL0_COLOR + ENVIRONMENT_COLOR and here are some common alpha combiner modes: Select = TEXEL0_ALPHA Multiply = TEXEL0_ALPHA * TEXEL1_ALPHA // See Rule 41 point
-
1 point
-
1 point
-
So I realized I never documented this when I was helping someone on skype, so I came back to post it. RECAP ================================================== As said in a couple of my older posts, the 0xFC GBI instruction sets the combiner mode. where the equation is: (a - * c + d;The substituted arguments encoded into 0xFC: a0, b0, c0, d0 = color mode 1Aa0, Ab0, Ac0, Ad0 = alpha mode 1 a1, b1, c1, d1 = color mode 2Aa1, Ab1, Ac1, Ad1 = alpha mode 2color mode 2 should always be set to the same as color mode 1 otherwise 2-cycle rendering happens.the most common blending mode in 3d rendering is 1-srcAlpha; (because the alpha channel actually represents opacity and this converts opacity to alpha; eg. opacity of 0.25, 1.0 - 0.25 = alpha of 0.75). the alpha mode for this would be: (1 - TEXEL0) * 1 + 0which is Aa0 = 6Ab0 = 1Ac0 = 6Ad0 = 7An additional note, K5 should be used in place of 0 when specifying the a0 or b0 fields of the color mode because the fields are 4-bit. THE COOL STUFF ================================================== The F3DZEX microde makes use of 3 tiles: 0, 1, and 7. Tile 0 is the main texture, tile 1 is the subtexture, and tile 7 is used to copy data into TMEM with F0 (for palettes) or F3 (for textures). Knowing this and the information in the recap above, multi-texturing is actually not hard at all. First, load TEXEL0 (tile 0): FD 90 00 00 02 00 22 60 ; set TIMG offset to 0x02002260F5 90 00 00 07 01 44 62 ; assign TIMG to tile 07 and assign it's propertiesE6 00 00 00 00 00 00 00 ; load syncF3 00 00 00 07 3F F1 00 ; copy tile 07 into TMEME7 00 00 00 00 00 00 00 ; pipe syncF5 88 10 00 00 01 44 62 ; assign TIMG to tile 00 and assign it's propertiesF2 00 00 00 00 0F C0 7C ; set tile 00 sizenext load TEXEL1 (tile 1): FD 90 00 00 02 00 22 60 ; set TIMG offset to 0x02002260F5 90 00 00 07 01 40 61 ; assign TIMG to tile 07 and assign it's propertiesE6 00 00 00 00 00 00 00 ; load syncF3 00 00 00 07 3F F1 00 ; copy tile 07 into TMEME7 00 00 00 00 00 00 00 ; pipe syncF5 88 10 00 01 01 40 61 ; assign TIMG to tile 01 and assign it's propertiesF2 00 00 00 01 0F C0 7C ; set tile 01 sizeThen, assign a blending mode. For example, to multiply them together evenly: (TEXEL0 - K5) * TEXEL1 + 0...or to blend additively such as done for the grain of grass in the game: (TEXEL1 - TEXEL0) * 1 + TEXEL0However, if you wanted to use the second texture as an alpha mask, you could do: color mode = (TEXEL0 - K5) * SHADE + PRIMITIVEalpha mode = (1 - TEXEL1) * 1 + 0And don't go apeshit and start hyperventilating if your multi-texture combiner mode can't support shading or primitive colors. That's what the COMBINED argument and color/alpha mode 2 are for. (the COMBINED and COMBINED_ALPHA can only be used color mode 2 and are color and alpha output after mode 1 is processed) To generate combiner instructions, you can use the source to a C program I wrote a while back called fcint. Also, just for the sake of reference, loading a palette... FD 10 00 00 02 00 78 60 ; set TIMG offset to 0x02002260E8 00 00 00 00 00 00 00 ; tile syncF5 00 01 F0 07 00 00 00 ; assign TIMG to tile 07 and assign it's propertiesE6 00 00 00 00 00 00 00 ; load syncF0 00 00 00 07 03 C0 00 ; copy tile 07 into TMEM ((3C0 >> 6) + 1) == 16 colorsE7 00 00 00 00 00 00 00 ; pipe syncEDIT================================================ Just something interesting I found out, 0xDB MOVEWORD can be used to set the physical addresses for segments. DB 06 00 18 80 17 00 00Explanation:[*]DB = MOVEWORD (or rather, write word) to an address specified in a table in dmem [*]06 = index of the segment table offset in the address table [*]0018 = offset to add to address (in this case, 4 * segment number because each address in the segment table is 4 bytes) [*]80170000 = the 32-bit word to write at address[index]+offset A heads up though, the game usually just uses assembly code to set segment offsets. There are only 6 MOVEMEM instructions in the entire game all located within 1 display list in the code file used to set segments 08, 09, 0A, 0B, 0C, and 0D to 0x801270981 point
-
Someone requested a clipping code from me and I didn't have it written down so I had to kinda re-find it. $801DB0B2 // player state%10000000 // lock player state%00001000 // disable jump attack%00000100 // disable player animation / movement updating%00000010 // disable jumping%00000001 // disable map collisionWARNING: You must manually change these back to 0 to undo them, so like if you disable map collision, you cannot touch the ground again until you reset it to 0.Hold Z To Lock Player State-----------------------------------------D01C84B4 0000801DB0B2 0000D01C84B4 0020801DB0B2 0080Hold Z to Lock Player Animation, Speed, and Direction-----------------------------------------D01C84B4 0000801DB0B2 0000D01C84B4 0020801DB0B2 0004Press Z To Disable Collision (walk through walls and floors)-----------------------------------------D01C84B4 0000801DB0B2 0000D01C84B4 0020801DB0B2 0001Disable Jumping-----------------------------------------801DB0B2 0002Disable Jump Attack (Link will still hop and shout)-----------------------------------------801DB0B2 00081 point