This tutorial uses this game: SOCOM II [SCUS_972.75]
Update: Added examples for multiple games.
This tutorial explains how to print text to the screen using SOCOM II. This is a method tutorial as every game will be different. This tutorial assumes you are emulating the game and using PCSX2DIS.
The code in the tutorial was created by Harry62 and Antix(aka Chronotrigga)
Example image:

Step 1: Finding the print text function
- The easiest text to find is the ammo count. Start by looking at your ammo which is most likely "30/30". Fire your weapon and perform a string search for "29/30".
- Once you have the pointer for that string set a read breakpoint on that address and see what loads it. There will be several functions loading this string pointer. Take note of which functions access it. Now find another string that is printed to the screen such as a start menu item. Repeat the read breakpoint and note the functions that load the string pointer. There should only be one or two for SOCOM 2 that is loading both string pointers excluding the PS2 library functions such as "strcpy" or "strcmp"(do note for other games that these functions may be useful for pinpointing the print text function).
- If you have multiple functions accessing the string pointers then try function cutting one to see if all text disappears. If it does then you may have the correct function. This part may take some more debugging depending on the game, however for SOCOM 2 this function prints the text:
PRINT_TEXT: 003635C0
Step 2: Understanding how the function works
- Now that you have the function set a breakpoint on it to see how it works.
- You will notice two args sent to the function. a0 and a1. You should also notice that the string pointer is NOT one of the args. With the breakpoint set you will now need to look through both arguments to better understand what is happening. You will notice the string pointer we found earlier is located in the a0 stack.
- If you continue looking through the a0 stack you will find the text size, colors, offsets, and eventually the next text pointer stack.
- For SOCOM 2 the stack is 104 bytes in size. This stack is needed for the PRINT_TEXT function.
- At this point copy the address for a0 and the address for a1. Now try sending those args to the PRINT_TEXT function with a routine that constantly calls the function. The text should render to the screen even if you press start or select.
Step 3: Create a function for rendering text
- We need an easy way to print text. Take note of how each a0 stack looks in the PRINT_TEXT function. Debug the stack until you know what everything does. Some data will not change anything on screen while removing other data will freeze the game. We will refer to this unknown data as filler data.
- Create a function that fills in the filler data by default. For everything else such as the text pointer, X&Y offsets, size, color, etc, we will set those variables up as arguments.
An example function is below for printing "Hello World".
This is the basic code to print text to the screen.
You can take it much further and create multiple lines such as a memory viewer:

Examples in other games:
Twisted Metal Black Online:
Call of Duty 3:
Update: Added examples for multiple games.
This tutorial explains how to print text to the screen using SOCOM II. This is a method tutorial as every game will be different. This tutorial assumes you are emulating the game and using PCSX2DIS.
The code in the tutorial was created by Harry62 and Antix(aka Chronotrigga)
Example image:
Step 1: Finding the print text function
- The easiest text to find is the ammo count. Start by looking at your ammo which is most likely "30/30". Fire your weapon and perform a string search for "29/30".
- Once you have the pointer for that string set a read breakpoint on that address and see what loads it. There will be several functions loading this string pointer. Take note of which functions access it. Now find another string that is printed to the screen such as a start menu item. Repeat the read breakpoint and note the functions that load the string pointer. There should only be one or two for SOCOM 2 that is loading both string pointers excluding the PS2 library functions such as "strcpy" or "strcmp"(do note for other games that these functions may be useful for pinpointing the print text function).
- If you have multiple functions accessing the string pointers then try function cutting one to see if all text disappears. If it does then you may have the correct function. This part may take some more debugging depending on the game, however for SOCOM 2 this function prints the text:
PRINT_TEXT: 003635C0
Step 2: Understanding how the function works
- Now that you have the function set a breakpoint on it to see how it works.
- You will notice two args sent to the function. a0 and a1. You should also notice that the string pointer is NOT one of the args. With the breakpoint set you will now need to look through both arguments to better understand what is happening. You will notice the string pointer we found earlier is located in the a0 stack.
- If you continue looking through the a0 stack you will find the text size, colors, offsets, and eventually the next text pointer stack.
- For SOCOM 2 the stack is 104 bytes in size. This stack is needed for the PRINT_TEXT function.
- At this point copy the address for a0 and the address for a1. Now try sending those args to the PRINT_TEXT function with a routine that constantly calls the function. The text should render to the screen even if you press start or select.
Step 3: Create a function for rendering text
- We need an easy way to print text. Take note of how each a0 stack looks in the PRINT_TEXT function. Debug the stack until you know what everything does. Some data will not change anything on screen while removing other data will freeze the game. We will refer to this unknown data as filler data.
- Create a function that fills in the filler data by default. For everything else such as the text pointer, X&Y offsets, size, color, etc, we will set those variables up as arguments.
An example function is below for printing "Hello World".
This is the basic code to print text to the screen.
Code:
address $200C8000
print "Hello World"
// ----------------------------------------------
// print text to screen
// -- This hook constantly writes the text to the screen.
// -- NOTE: Text will disappear once the screen refreshes, therefore
// -- you must constant write the text.
address $2030DBA8
j $000c9000
address $200c9000
addiu sp, sp, $FF80
sw ra, $0000(sp)
// -- print "Hello World"
setreg a0, $000C8000 // string ptr
setreg a1, $42f00000 // X offset (from left side of screen)
setreg a2, $43000000 // Y offset (from top side of screen)
setreg a3, $3f800000 // size
setreg t0, $43000000 // Red
setreg t1, $43000000 // Green
setreg t2, $00000000 // Blue
setreg t3, $43000000 // Alpha
jal :__print_text
nop
lw ra, $0000(sp)
jr ra
addiu sp, sp, $80
//----------------------------------------------
// print text fnc
/*
args:
a0: ptr to text to render
a1: X offset
a2: Y offset
a3: text size
t0: text color RED
t1: text color GREEN
t2: text color BLUE
t3: text color ALPHA
NOTE: There is a lot of data in this fnc that is not explained in the comments.
This data was found in every text string I examined. It is needed for the
SOCOM 2 text printing function.
*/
__print_text:
// create stack pointer
addiu sp, sp, $FF00
sw ra, $0000(sp)
sw s0, $0004(sp)
// This is where the data is stored for using this function
setreg s0, $000CA000 // pointer setup start
setreg v0, $00406DF0
sw v0, $000c(s0)
setreg v0, $3F8000CD
sw v0, $0014(s0)
// Set text pointer
sw a0, $001c(s0)
addiu v0, zero, $000F
sw v0, $0020(s0)
addiu v0, zero, $0006
sw v0, $0024(s0)
setreg v0, $414ED2E3
sw v0, $0038(s0)
setreg v0, $42740000
sw v0, $003c(s0)
// set text size
sw a3, $0040(s0)
// set text RGBA
sw t0, $0048(s0)
sw t1, $004c(s0)
sw t2, $0050(s0)
sw t3, $0054(s0)
setreg v0, $80800051
sw v0, $005c(s0)
setreg v0, $0040D57C
lw v0, $0000(v0)
sw v0, $0018(s0)
setreg v0, $004067B0
sw v0, $0060(s0)
addiu v0, zero, $0100
sw v0, $0068(s0)
addiu v0, zero, $015e
sw v0, $0070(s0)
setreg v0, $3F800000
sw v0, $0078(s0)
// set X offset
sw a1, $0090(s0)
// set Y offset
sw a2, $0094(s0)
setreg v0, $0000EC60
sw v0, $0098(s0)
setreg v0, $3F801B00
sw v0, $009c(s0)
setreg v0, $00406C10
sw v0, $0100(s0)
setreg v0, $00488DF8
lw v0, $0000(v0)
sw v0, $0104(s0)
// Print text to screen
daddu a0, s0, zero // a0 = start of text pointer stack
addiu a1, s0, $0100 // set a1 to pointer in text pointer stack
jal $003635C0 // print text fnc (native to socom 2)
nop
// pop stack pointer
lw s0, $0004(sp)
lw ra, $0000(sp)
jr ra
addiu sp, sp, $0100
You can take it much further and create multiple lines such as a memory viewer:
Examples in other games:
Twisted Metal Black Online:
Code:
/* Use this function to print text. It must be called constantly. Printing text steps: 1: Set text color by using this function: 00202B78 a0: alpha a1: red a2: green a3: blue 2: Print text to screen using this function: 00202DF8 a0: text size (1 = small, 0 = large) a1: Screen X position (from left) a2: Screen Y position (fromt top) a3: String pointer (for text) */ // HOOK (this calls my function constantly to render text) address $201399A0 j :__TEXT_RENDER // My function start address address $200A0000 __TEXT_RENDER: addiu sp, sp, $FF80 sw ra, $0000(sp) // set text color addiu a0, zero, $0000 // alpha addiu a1, zero, $00a0 // red addiu a2, zero, $0010 // green jal $00202B78 // SET TEXT COLOR FUNCTION addiu a3, zero, $0000 // blue // render text addiu a0, zero, $1 // text size (1 = small, 0 = large) addiu a1, zero, $0050 // X position addiu a2, zero, $0050 // Y position setreg a3, $000B0000 // text pointer jal $00202DF8 // PRINT TEXT TO SCREEN FUNCTION nop lw ra, $0000(sp) jr ra addiu sp, sp, $80 // text address $200B0000 print "Hello World"
Code:
/* This function prints text to the screen. It must be called constantly to render text. */ // HOOK address $201F8544 //-- constant hook //202308E8 //-- in game hook j $000A0000 // My function start address address $200A0000 addiu sp, sp, $FF80 sw ra, $0000(sp) // example "Hello World" setreg a0, $000B0000 // text ptr setreg a1, $42480000 // X offset setreg a2, $42D40000 // Y offset setreg a3, $3f000000 // text size width setreg t0, $3f000000 // text size height setreg t1, $FFFFAF00 // text color ARGB jal :__PRINT_TEXT nop lw ra, $0000(sp) jr ra addiu sp, sp, $80 __PRINT_TEXT: /* a0 - text pointer a1 - X offset a2 - Y offset a3 - text size width t0 - text size height t1 - text color ARGB */ addiu sp, sp, $FF80 sw ra, $0000(sp) sw s0, $0004(sp) sw s1, $0008(sp) // stack location. this can be changed to any blank area. setreg s1, $000A1000 // default data lui s0, $3f80 sw s0, $0000(s1) lui s0, $41A0 sw s0, $0004(s1) addiu s0, zero, $4 sw s0, $000C(s1) setreg s0, $0079E290 // text fnc stack sw s0, $0010(s1) // default data lui s0, $C387 sw s0, $0020(s1) // store X and Y screen offsets sw a1, $002C(s1) sw a2, $0030(s1) // default data lui s0, $3F80 sw s0, $0038(s1) sw s0, $003C(s1) sw s0, $0044(s1) sw s0, $0048(s1) // store size sw a3, $0050(s1) sw t0, $0054(s1) // store color sw t1, $0060(s1) // ARGB // default data addiu s0, zero, $15 sw s0, $006C(s1) // save text pointer pointer (pointer1 >> pointer2 >> string) // s1+100 = pointer1 start. This is needed for the native print text function. addiu s0, s1, $100 // set pointer1 sw s0, $001C(s1) addiu s0, s1, $10C // set pointer2 sw s0, $0100(s1) setreg s0, $00400040 // text length set high sw s0, $0104(s1) // copy text pointer string to stack pointer daddu a1, a0, zero // source addiu a0, s1, $10C // location to copy to jal $0050EEF0 //strcpy addiu a2, zero, $100 // PRINT TEXT daddu a0, s1, zero addiu a1, zero, $0 jal $001D0A5C // native print text fnc nop lw ra, $0000(sp) lw s0, $0004(sp) lw s1, $0008(sp) jr ra addiu sp, sp, $80 // text to print address $200B0000 print "Hello World" nop
Comment