V-Report v0.1 [Vega]
So I threw this code into the Code Help/Support sub-forum because I'm too lazy to finish testing (lol). And this code is very un-optimized with some issues I'll explain later.
This is meant to replace any exception handlers you have. The code will create a report.txt file in your /shared2 directory of your NAND giving many details of your exception. If the crash is a DSI, ISI, Alignment, or Program exception, V-Report will attempt to generate a report on it.
It's designed to work for all Wii games. Thus, it's region free ofc, just slap the code on any game and you're good to go.
NOTE: This code easily exceeds the typical GCT limit, you need to add this code via main.dol patching of some sorts (i.e. Wiimm's wstrt or Joshua's GeckoLoader)
Video demo :
What's included in the report:
Equip this code with your other codes. Once a crash occurs and V-Report was successful generating a report, your screen will turn green, hard reboot your Wii, use some HBC app to view the /shared2/report.txt. If the screen is yellow, V-Report failed. Please note that the screen colors won't work on Dolphin, this is an issue with Dolphin, not the code. So as of right now, this code is only meant for real hardware use. It will still work on Dolphin, but your emulation will just freeze up and be glitchy, just stuck there until you shut it off.
---
Technical details/issues (noobs may not understand some of this)
First thing's first, some of the comments/notes on the sources may not be accurate, just an fyi, as I was constantly making small new changes along the way. V-Report functino pointer is saved into sprg3 (never used by any Wii game), and all the report data is stored in exception areas that are not hit by the string writes, in the gecko code handler itself (safe to do since we already crashed), and the unused Thermal interrupt.
V-Report can only fail in two main areas. The first is not being able to find the game's sprintf function. The second is some error return when fiddling with the IPC registers via my Universal IOS code.
The reason why I don't end the code off with something such as a OSFatal message, is because on some of my tests on diff Wii games, the game crashes on OSFatal! No idea why this is. Also tried some tests with OSReturnToMenu on certain tests, that would crash as well and that function literally takes zero args, you just call it.
A solution to all of this could be to write some code to setup the VI interface manually and eliminate all visual graphics and just have the console up and output printf messages on the TV screen, but that would NOT be a fun task.
Tests done so far:
DSI on PAL MKWii on real hardware
DSI on PAL MKWii on Dolphin
DSI on NTSC-U DBZ BT3 on Dolphin
DSI on NTSC-U Brawl on Dolphin
---
Planned improvements to V-Report (if I ever get around to it)
Mute the audio so the crash has no annoying noises (easy to do, just too lazy to work on this code lol)
Do something other than screen colors (custom console printf messages, hard to do)
Add more registers to the report such as all FPRs also being stored as paired singles (easy to do, just too lazy)
---
Without further a do, here's the code and the sources. Everything is licensed under GPLv2. A notice of the license is included in both sources.
Code creator: Vega
Code credits: Segher, Tmbinc, & Bushing (authors of the original ipc.c on WiiBrew)
First Source (06 string writes)
Second Source (C0 code)
So I threw this code into the Code Help/Support sub-forum because I'm too lazy to finish testing (lol). And this code is very un-optimized with some issues I'll explain later.
This is meant to replace any exception handlers you have. The code will create a report.txt file in your /shared2 directory of your NAND giving many details of your exception. If the crash is a DSI, ISI, Alignment, or Program exception, V-Report will attempt to generate a report on it.
It's designed to work for all Wii games. Thus, it's region free ofc, just slap the code on any game and you're good to go.
NOTE: This code easily exceeds the typical GCT limit, you need to add this code via main.dol patching of some sorts (i.e. Wiimm's wstrt or Joshua's GeckoLoader)
Video demo :
What's included in the report:
- Address that CPU crashed on (also known as srr0)
- Instruction at said Address
- Type of Exception that occurred
- Stack Trace
- Callstack
- GPRs
- FPRs (stored as double precision via stfd)
- Misc Registers (srr1, DAR, DSISR, LR, CTR, CR, XER, FPSCR, HID0, HID2, HID4, L2CR)
Equip this code with your other codes. Once a crash occurs and V-Report was successful generating a report, your screen will turn green, hard reboot your Wii, use some HBC app to view the /shared2/report.txt. If the screen is yellow, V-Report failed. Please note that the screen colors won't work on Dolphin, this is an issue with Dolphin, not the code. So as of right now, this code is only meant for real hardware use. It will still work on Dolphin, but your emulation will just freeze up and be glitchy, just stuck there until you shut it off.
---
Technical details/issues (noobs may not understand some of this)
First thing's first, some of the comments/notes on the sources may not be accurate, just an fyi, as I was constantly making small new changes along the way. V-Report functino pointer is saved into sprg3 (never used by any Wii game), and all the report data is stored in exception areas that are not hit by the string writes, in the gecko code handler itself (safe to do since we already crashed), and the unused Thermal interrupt.
V-Report can only fail in two main areas. The first is not being able to find the game's sprintf function. The second is some error return when fiddling with the IPC registers via my Universal IOS code.
The reason why I don't end the code off with something such as a OSFatal message, is because on some of my tests on diff Wii games, the game crashes on OSFatal! No idea why this is. Also tried some tests with OSReturnToMenu on certain tests, that would crash as well and that function literally takes zero args, you just call it.
A solution to all of this could be to write some code to setup the VI interface manually and eliminate all visual graphics and just have the console up and output printf messages on the TV screen, but that would NOT be a fun task.
Tests done so far:
DSI on PAL MKWii on real hardware
DSI on PAL MKWii on Dolphin
DSI on NTSC-U DBZ BT3 on Dolphin
DSI on NTSC-U Brawl on Dolphin
---
Planned improvements to V-Report (if I ever get around to it)
Mute the audio so the crash has no annoying noises (easy to do, just too lazy to work on this code lol)
Do something other than screen colors (custom console printf messages, hard to do)
Add more registers to the report such as all FPRs also being stored as paired singles (easy to do, just too lazy)
---
Without further a do, here's the code and the sources. Everything is licensed under GPLv2. A notice of the license is included in both sources.
Code creator: Vega
Code credits: Segher, Tmbinc, & Bushing (authors of the original ipc.c on WiiBrew)
Code:
06000300 00000024
7C1043A6 7C0802A6
7C1143A6 48000005
7C0902A6 7C1243A6
7C1342A6 7C0903A6
4E800420 00000000
06000400 00000024
7C1043A6 7C0802A6
7C1143A6 48000005
7C0902A6 7C1243A6
7C1342A6 7C0903A6
4E800420 00000000
06000600 00000024
7C1043A6 7C0802A6
7C1143A6 48000005
7C0902A6 7C1243A6
7C1342A6 7C0903A6
4E800420 00000000
06000700 00000024
7C1043A6 7C0802A6
7C1143A6 48000005
7C0902A6 7C1243A6
7C1342A6 7C0903A6
4E800420 00000000
C0000000 00000112
7C0802A6 48000875
7C1042A6 BC001700
7E9142A6 7EB242A6
7EC00026 7EE102A6
7F1A02A6 7F3B02A6
7F5302A6 7F7202A6
7F90FAA6 7FB8E2A6
7FD3FAA6 7FF9FAA6
BE800324 7FC802A6
480001F9 9421FF60
7C0802A6 900100A4
BF61008C 7C7B1B78
40860024 D8210028
562D5265 706F7274
20627920 56656761
206F6620 4D4B5769
692E636F 6D0A0A41
64647265 73732043
50552063 72617368
6564206F 6E202873
72723029 3A202530
38582049 6E737472
75637469 6F6E2040
20416464 72657373
3A202530 38580A0A
45786365 7074696F
6E205479 70653A20
25303858 0A0A4441
523A2025 30385820
44534953 52202530
38580A0A 53746163
6B205472 6163650A
41646472 65737320
4261636B 63686169
6E204C52 2D536176
65000A25 30385820
25303858 20253038
58000A0A 43616C6C
73746163 6B0A2530
38580A25 3038580A
25303858 0A253038
580A2530 3858000A
0A475052 733A0A72
25316420 25303858
0A722531 64202530
3858000A 72253164
20253038 580A7225
31642025 30385800
0A0A4650 52733A0A
66253164 20253038
58253038 580A6625
31642025 30385825
30385800 0A662531
64202530 38582530
38580A66 25316420
25303858 25303858
000A0A4D 6973633A
0A4C5220 25303858
0A435452 20253038
580A4352 20253038
580A7372 72312025
3038580A 58455220
25303858 000A4650
53435220 25303858
0A484944 30202530
38580A48 49443220
25303858 0A484944
34202530 38580A4C
32435220 25303858
002F7368 61726564
322F7265 706F7274
2E747874 00000000
7FE802A6 387F0220
64638000 7C7A03A6
7C7B02A6 5463045E
60632000 54630566
5463062C 7C7B03A6
4C000064 3FA08000
67FF8000 D83D1800
D83D1808 D85D1810
D87D1818 D89D1820
D8BD1828 D8DD1830
D8FD1838 D91D1840
D93D1848 D95D1850
D97D1858 D99D1860
D9BD1868 D9DD1870
D9FD1878 DA1D1880
DA3D1888 DA5D1890
DA7D1898 DA9D18A0
DABD18A8 DADD18B0
DAFD18B8 DB1D18C0
DB3D18C8 DB5D18D0
DB7D18D8 DB9D18E0
DBBD18E8 DBDD18F0
DBFD18F8 FC00048E
D801FFF8 8001FFFC
901D1900 63A50424
7C2A0B78 3C800001
38000005 7C0903A6
816A0000 818B0000
7C6B6050 7C032040
91450000 91650004
800A0004 90050008
38A5000C 4181000C
7D6A5B78 4200FFD4
63A30620 3A94FFFC
96830004 63A4042C
38000004 7C0903A6
84A4000C 2C050000
41820008 38A5FFFC
94A30004 4200FFEC
7DFFE4AA 3C00005F
6000F3FF 7C0903A6
63A33000 7CA3E4AA
7C057800 40820034
7C068000 4082002C
7C078800 40820024
7C089000 4082001C
7C099800 40820014
7C0AA000 4082000C
7C0BA800 41820010
38630004 4200FFC0
480002CC 7C6E1B78
38610040 54630034
7C7C1B78 389F001C
80BD0334 80C50000
57C7002E 811D033C
813D0340 7DC903A6
4E800421 7C7B1B78
63AF0420 3A000005
7C7BE214 389F00CE
84AF0004 84CF0004
84EF0004 7DC903A6
4E800421 2C030000
4180026C 7F7B1A14
3610FFFF 4082FFD4
7C7BE214 389F00DE
80BD0624 80DD0628
80FD062C 811D0630
813D0634 7DC903A6
4E800421 2C030000
41800234 7F7B1A14
7C7BE214 389F0103
38A00000 80DD1700
38E00001 811D1704
7DC903A6 4E800421
2C030000 41800208
7F7B1A14 63AF1704
3A00000F 3A200002
3A400003 7C7BE214
389F011F 7E258B78
84CF0004 7E479378
850F0004 7DC903A6
4E800421 2C030000
418001CC 7F7B1A14
3A310002 3A520002
3610FFFF 4082FFC8
7C7BE214 389F0134
38A00000 80DD1800
80FD1804 39000001
813D1808 815D180C
7DC903A6 4E800421
2C030000 41800188
7F7B1A14 63AF180C
3A00000F 3A200002
3A400003 7C7BE214
389F0158 7E258B78
84CF0004 84EF0004
7E489378 852F0004
854F0004 7DC903A6
4E800421 2C030000
41800144 7F7B1A14
3A310002 3A520002
3610FFFF 4082FFC0
7C7BE214 389F0175
80BD0324 80DD0328
80FD032C 811D0338
813D0330 7DC903A6
4E800421 2C030000
41800104 7F7B1A14
7C7BE214 389F01A9
80BD1900 80DD0344
80FD0348 811D034C
813D0350 7DC903A6
4E800421 2C030000
418000D4 7F7B1A14
38000013 7C0903A6
38000000 63A3207C
94030004 4200FFFC
387F01D9 63A42082
38000005 7C0903A6
84030004 94040004
4200FFF8 38000003
981D20C6 981D20C7
981D20C8 38600006
63A42020 38A00002
38C00009 63A72080
3900004C 4800008D
2C030000 41800068
38600001 63A42020
38BF01DD 38C00002
48000071 2C030000
4180004C 7C7A1B78
7C651B78 38600004
63A42020 7F86E378
7F67DB78 4800004D
2C030000 41800028
38600002 63A42020
7F45D378 48000035
2C030000 41800010
3C60CD80 3C000000
4800000C 3C60CD80
3C0000FF 6000FF01
90030024 7C0004AC
60000000 4BFFFFF8
9421FFF0 7C0802A6
90010014 BFC10008
7C7F1B78 7C9E2378
2C1F0001 41820034
2C1F0002 4182005C
2C1F0006 4182005C
7CC33378 7CE43B78
48000081 90BE0008
54C0007E 901E000C
90FE0010 480000B8
3865FFFF 38800000
38840001 8C030001
2C000000 4082FFF4
7CA32B78 4800004D
54A0007E 901E000C
90DE0010 48000088
90BE0008 48000080
7CE33B78 7D044378
48000029 90BE0008
90DE000C 54E0007E
901E0010 911E0014
5520007E 901E0018
915E001C 48000050
546B06FE 7C845A14
3884001F 5480D97E
7C0903A6 7C0018AC
38630020 4200FFF8
44000002 4E800020
546B06FE 7C845A14
3884001F 5480D97E
7C0903A6 7C001BAC
38630020 4200FFF8
4E800020 93FE0000
7FC3F378 38800020
4BFFFFA9 3CC0CD00
57C0007E 90060000
80060004 54000036
60000001 90060004
80060004 70000022
2C000022 40A2FFF4
80060004 54000036
60000002 90060004
3CA04000 90A60030
80060004 70000014
2C000014 40A2FFF4
80860008 2C040000
3860FFFB 41820038
3C648000 38800020
4BFFFF61 80060004
54000036 60000004
90060004 90A60030
80060004 54000036
60000008 90060004
807E0004 BBC10008
80010014 7C0803A6
38210010 4E800020
7D6802A6 556B007E
7D7343A6 7C0803A6
4E800020 00000000
First Source (06 string writes)
Code:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
#First Source (backup some things, call V-Report)
#START ASSEMBLY
#Write at all following addresses via 06 Gecko String Writes
#0x80000300; for DSI
#0x80000400; for ISI
#0x80000600; for Alignment
#0x80000700; for Program
#Backup r0
mtsprg0 r0
#Backup LR
mflr r0
mtsprg1 r0
#Do BL Trick to get Program Counter so V-Report can figure out what kind of exception occurred
bl getpc
getpc:
#Backup CTR
mfctr r0
mtsprg2 r0
#Get pointer to V-Report function; it's in sprg3
mfsprg3 r0
#Call V-Report!; pointer is already physical, good to go
mtctr r0
bctr
#END ASSEMBLY
Second Source (C0 code)
Code:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
#============================
#Second Source (V-Report)
#START ASSEMBLY
#Put the entire code into one gigantic Brach-link table. We do this so the code doesn't have to reside in the unused EVA space via an 06 string write. Also the contents of a 06 string write are copy-pasted from the GCT to the designated hook address every frame. This will bog down on CPU preformance since the transfer of the 06-string write data occurs every frame.
#A better approach is to have our code within a C0 code but the C0 code will simply consist of storinig teh pointer to the codes contents at a spot in the EVA. This greatly helsp on CPU preofrmance and this mthod only tkes up one word of unused EVA space.
#First thing's first, backup C0 LR
mflr r0
#Create gignatic BL Trick
bl the_entire_code
########################################################
#The code itself
#Note
#r0 treated as literal 0; nice to use for physical work
#Variables
.set EVA_GPR, 0x1700 #Where GPRs are stored at
.set EVA_FPR, 0x1800 #Where FPRs are stored at
.set EVA_MISC, 0x324 #Where Misc registers are stored at
.set EVA_STACK_TRACE, 0x424 #Where the 5 rows Stack Trace contents are stored at
.set EVA_CALLSTACK, 0x624 #5 words of the call stack
#.set EVA_LR, EVA_MISC+0 #Not needed, just listed here for reference
.set EVA_CTR, EVA_MISC+4
.set EVA_CR, EVA_MISC+8
.set EVA_XER, EVA_MISC+12
.set EVA_srr0, EVA_MISC+16
.set EVA_srr1, EVA_MISC+20
.set EVA_DAR, EVA_MISC+24
.set EVA_DSISR, EVA_MISC+28
.set EVA_HID0, EVA_MISC+32
.set EVA_HID2, EVA_MISC+36
.set EVA_HID4, EVA_MISC+40
.set EVA_L2CR, EVA_MISC+44
.set EVA_FPSCR, EVA_FPR+256
.set HID0, 1008
.set HID2, 920
.set HID4, 1011
.set L2CR, 1017
.set DSISR, 12
.set DAR, 19
#Recover original r0
mfsprg0 r0
#Store every gpr starting to the unused thermal interrupt
stmw r0, EVA_GPR (r0)
#We cant store any FPRs in physical mode because...
#It will cause an FP unavailable exception due to MSR being set to 0x00001000 when the exception occured.
#Store misc other registers to the portion of the DSI vector that the 06 writes aren't using
#There are a crap ton more registers but honestly they having and will have nothing to do with execptions
mfsprg1 r20 #original LR
mfsprg2 r21 #original CTR
mfcr r22
mfxer r23
mfspr r24, srr0 #Address CPU crashed on
mfspr r25, srr1 #MSR value when crash occurred
mfspr r26, DAR
mfspr r27, DSISR
mfspr r28, HID0
mfspr r29, HID2
mfspr r30, HID4
mfspr r31, L2CR
stmw r20, EVA_MISC (r0)
#LR contains the exception type, place it in r30 since we need to use the Link Register now
mflr r30
#Make table
bl table
table_start:
#Search parameters for sprintf
sprintf_search_words:
.long 0x9421FF60
.long 0x7C0802A6
.long 0x900100A4
.long 0xBF61008C
.long 0x7C7B1B78
.long 0x40860024
.long 0xD8210028
title:
.string "V-Report by Vega of MKWii.com\n\nAddress CPU crashed on (srr0): %08X\ Instruction @ Address: %08X\n\nException Type: %08X\n\nDAR: %08X DSISR %08X\n\nStack Trace\nAddress Backchain LR-Save"
stack_trace_row_string:
.string "\n%08X %08X %08X"
call_stack_start_string:
.string "\n\nCallstack\n%08X\n%08X\n%08X\n%08X\n%08X"
gpr_start_string:
.string "\n\nGPRs:\nr%1d %08X\nr%1d %08X"
gpr_string:
.string "\nr%1d %08X\nr%1d %08X"
fpr_start_string:
.string "\n\nFPRs:\nf%1d %08X%08X\nf%1d %08X%08X"
fpr_string:
.string "\nf%1d %08X%08X\nf%1d %08X%08X"
misc_start_string:
.string "\n\nMisc:\nLR %08X\nCTR %08X\nCR %08X\nsrr1 %08X\nXER %08X"
misc_final_string:
.string "\nFPSCR %08X\nHID0 %08X\nHID2 %08X\nHID4 %08X\nL2CR %08X"
file_path:
.string "/shared2/report.txt"
.align 2
table:
mflr r31 #Need to backup pointer to table for function calls
#Place start of virtual mode in r3
virtual_offset = end_physical - table_start
addi r3, r31, virtual_offset
#Make it virtual
oris r3, r3, 0x8000
#Place virtual address to jump to in srr0
mtspr srr0, r3
#We need to do some edits on the MSR (srr1). We need the following bits with the following settings..
#Remember you cannot edit the actual MSR register during an interrupt
#Bit 16 (EE aka External Interrupts) must be low
#Bit 18 (FP aka Floating Point Avialable) must be high
#Bit 20 (FE0 aka IEEE floating point exception mode 0) must be low
#Bit 23 (FE1 aka IEEE floating point exceptino mode 1) must be low
mfspr r3, srr1
rlwinm r3, r3, 0, 17, 15
ori r3, r3, 0x2000
rlwinm r3, r3, 0, 21, 19
rlwinm r3, r3, 0, 24, 22
mtspr srr1, r3
#Now finally ready to go to virtual mode!
rfi
#=======================
#Register notes
#r31 = Pointer to table (virtual)
#r30 = Exception Type
#r29 = 0x80000000
#r28 = Pointer to very start of 1st string
#r27 = Return Values of both sprintf calls added together
#r26 = fd
#r25 = 0x935E0000
#r18 = value for %1d
#r17 = secondary value for %1d
#r16 = loop tracker for sprintf loop writes
#r15 = temp addresses for the sprintfs
#r15 thru r22 = Search strings for sprinf/OShotreset; this is done before and after the group of sprintfs
#r14 = sprintf function address
#VIRTUAL MODE!
end_physical:
#Set r29, as 0x8000
lis r29, 0x8000
#Make r31 virtual
oris r31, r31, 0x8000
#Backup floats and fpscr to the System Management interrupt, high chance this is unused, but it doesn't matter regardless, lul
stfd f1, EVA_FPR (r29)
stfd f1, EVA_FPR+8 (r29)
stfd f2, EVA_FPR+16 (r29)
stfd f3, EVA_FPR+24 (r29)
stfd f4, EVA_FPR+32 (r29)
stfd f5, EVA_FPR+40 (r29)
stfd f6, EVA_FPR+48 (r29)
stfd f7, EVA_FPR+56 (r29)
stfd f8, EVA_FPR+64 (r29)
stfd f9, EVA_FPR+72 (r29)
stfd f10, EVA_FPR+80 (r29)
stfd f11, EVA_FPR+88 (r29)
stfd f12, EVA_FPR+96 (r29)
stfd f13, EVA_FPR+104 (r29)
stfd f14, EVA_FPR+112 (r29)
stfd f15, EVA_FPR+120 (r29)
stfd f16, EVA_FPR+128 (r29)
stfd f17, EVA_FPR+136 (r29)
stfd f18, EVA_FPR+144 (r29)
stfd f19, EVA_FPR+152 (r29)
stfd f20, EVA_FPR+160 (r29)
stfd f21, EVA_FPR+168 (r29)
stfd f22, EVA_FPR+176 (r29)
stfd f23, EVA_FPR+184 (r29)
stfd f24, EVA_FPR+192 (r29)
stfd f25, EVA_FPR+200 (r29)
stfd f26, EVA_FPR+208 (r29)
stfd f27, EVA_FPR+216 (r29)
stfd f28, EVA_FPR+224 (r29)
stfd f29, EVA_FPR+232 (r29)
stfd f30, EVA_FPR+240 (r29)
stfd f31, EVA_FPR+248 (r29)
mffs f0
stfd f0, -0x8 (sp)
lwz r0, -0x4 (sp)
stw r0, EVA_FPSCR (r29) #EVA_FPR+256
#========================
#Store Stack Trace (not this isnt the Call Stack)
#Address; Backchain; LR Save
#Store EVA Pointer to store at the space intended for Stack Trace contents (5 rows max output)
ori r5, r29, EVA_STACK_TRACE
#Make a copy of the sp for initial 'Address' value in Stack Trace output
mr 10, sp
#Stack sizes are never this large, this is our upper bound size limit check
lis r4, 0x0001
#Set max loop of 5
li r0, 5
mtctr r0
#Call stack search loop
trace_loop:
lwz r11, 0 (r10)
lwz r12, 0 (r11)
sub r3, r12, r11
#Check for upper bound on stack size limit
cmplw r3, r4
#Stack is valid, add on new stack trace row below
stw r10, 0 (r5) #Address
stw r11, 0x4 (r5) #Backchain
lwz r0, 0x4 (r10) #Load LR Save
stw r0, 0x8 (r5) #Store LR Save
#Increment r5 to point to next stack trace row
addi r5, r5, 0xC
#Do branch now based on cmpwi results
bgt- stack_trace_complete #If branching on this, that means we stored the last addr
#Follow further into the stack trace
mr r10, r11
bdnz+ trace_loop
stack_trace_complete:
#=====================
#Store Callstack (5 max)
#Callstack format...(just like Dolphin)
#addr = ... fyi the 1st addr is just LR-4.
#addr = ... fyi this is just the stack trace's 2nd LR-Save - 4
#addr = ... stack trace's 3rd LR-Save - 4
ori r3, r29, EVA_CALLSTACK-4
#Get top of call stack (OG Link Register - 4)
addi r20, r20, -4 #OG LR still residing in r20
stwu r20, 0x4 (r3)
#Loop to load 2nd thru 5th LR-Save of Stack trace, minus 4 on it, then store
ori r4, r29, EVA_STACK_TRACE+8
#Loop of 4
li r0, 4
mtctr r0
callstack_loop:
lwzu r5, 0xC (r4)
cmpwi r5, 0 #If an LR-save was null, call stack addr was null ofc
beq- no_minus_four
subi r5, r5, 4
no_minus_four:
stwu r5, 0x4 (r3)
bdnz+ callstack_loop
#==============================
#Attempt to find the game's sprintf function
lswi r15, r31, 28 #Load the sprintf search string into r5 thru r11, thank you lswi
#5FF3FF words from 0x80003000 to 0x817FFFFC
lis r0, 0x5F
ori r0, r0, 0xF3FF
mtctr r0
ori r3, r29, 0x3000
find_sprintf:
lswi r5, r3, 28
cmpw r5, r15
bne- decrement_sprintf_search
cmpw r6, r16
bne- decrement_sprintf_search
cmpw r7, r17
bne- decrement_sprintf_search
cmpw r8, r18
bne- decrement_sprintf_search
cmpw r9, r19
bne- decrement_sprintf_search
cmpw r10, r20
bne- decrement_sprintf_search
cmpw r11, r21
beq- found_sprintf
decrement_sprintf_search:
addi r3, r3, 4
bdnz+ find_sprintf
b HUGE_ERROR
#Found it! store in r14
found_sprintf:
mr r14, r3
#Call sprintf for 1st string
#r3 = arg to dump shit to
#r4 = pointer to string that needs formatting
#r5+ = args
title_string_offset = title - table_start
#We need to make sure r3 will end up being 0x20 (32) byte aligned, later for ISFS_Write
#Increment sp by 0x40 then clear last 5 bits, so we have a 32-byte aligned r3 arg
addi r3, sp, 0x40
clrrwi r3, r3, 5
we_are_aligned:
mr r28, r3 #Backup for the write buffer input on ISFS_Write
addi r4, r31, title_string_offset #Points to start of string in the table
lwz r5, EVA_srr0 (r29) #Load srr0's original value (address CPU crashed on)
lwz r6, 0 (r5) #Load srr0's address's instruction
clrrwi r7, r30, 8 #Clear out last byte, r7 will ontain 0x300, 0x400, 0x600, or 0x700
lwz r8, EVA_DAR (r29) #Load DAR's value
lwz r9, EVA_DSISR (r29) #Load DSISR's value
mtctr r14
bctrl
#Backup value of bytes written into r27, ISFS Write will need it later
mr r27, r3
#====================
#Sprintf loop for the 5 stack trace rows
#Setup loop first laod address
ori r15, r29, EVA_STACK_TRACE-4
#Set loop amount; need to use a GVR due to function call within loop
li r16, 5
#Set macro to get the sprintf string offset in our table
stack_trace_string_offset = stack_trace_row_string - table_start
stack_trace_sprintf_loop:
add r3, r27, r28 #Add return value to r27 to keep movign the sprintf dump location properly further down in memory
addi r4, r31, stack_trace_string_offset
lwzu r5, 0x4 (r15)
lwzu r6, 0x4 (r15)
lwzu r7, 0x4 (r15)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
subic. r16, r16, 1
bne+ stack_trace_sprintf_loop
#==================
#sprintf for call_stack_start_string
call_stack_string_offset = call_stack_start_string - table_start
add r3, r27, r28 #Update r3 to move further down in memory to keep dumping
addi r4, r31, call_stack_string_offset
lwz r5, EVA_CALLSTACK (r29)
lwz r6, EVA_CALLSTACK+4 (r29)
lwz r7, EVA_CALLSTACK+8 (r29)
lwz r8, EVA_CALLSTACK+12 (r29)
lwz r9, EVA_CALLSTACK+16 (r29)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
#==================
#1st GPR sprintf (its 'title' aka gpr_start_string)
gpr_title_string_offset = gpr_start_string - table_start
add r3, r27, r28 #Keep updating dump spot
addi r4, r31, gpr_title_string_offset
li r5, 0
lwz r6, EVA_GPR (r29)
li r7, 1
lwz r8, EVA_GPR+4 (r29)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
#===================
#Sprintf loop for the rest of the GPRs
#Set Loop first load address
ori r15, r29, EVA_GPR+4
#Set loop amount
li r16, 15 #30 gprs left, can only do 2 at a time
#Set the initial %1d's (starts at 3 and 4, cuz 1 and 2 already done)
li r17, 2
li r18, 3
#macro to set r4
gpr_loop_string_offset = gpr_string - table_start
gpr_sprintf_loop:
add r3, r27, r28 #Keep updating dump spot
addi r4, r31, gpr_loop_string_offset
mr r5, r17
lwzu r6, 0x4 (r15)
mr r7, r18
lwzu r8, 0x4 (r15)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
#Update the %1d's
addi r17, r17, 2
addi r18, r18, 2
subic. r16, r16, 1
bne+ gpr_sprintf_loop
#====================
#1st FPR sprintf (its 'title' aka fpr_start_string)
fpr_title_string_offset = fpr_start_string - table_start
add r3, r27, r28 #Keep updating dump spot
addi r4, r31, fpr_title_string_offset
li r5, 0
lwz r6, EVA_FPR (r29)
lwz r7, EVA_FPR+4 (r29)
li r8, 1
lwz r9, EVA_FPR+8 (r29)
lwz r10, EVA_FPR+12 (r29)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
#====================
#sprintf loop for rest of FPRs
#Setup loop first laod address
ori r15, r29, EVA_FPR+12
fpr_loop_string_offset = fpr_string - table_start
#Set loop amount
li r16, 15 #Can only do 2 fprs at a time cuz they are 16 hex digits in length
#Set the initial %1d's (starts at 3 and 4, cuz 1 and 2 already done)
li r17, 2
li r18, 3
fpr_sprintf_loop:
add r3, r27, r28 #Keep updating dump spot
addi r4, r31, fpr_loop_string_offset
mr r5, r17
lwzu r6, 0x4 (r15)
lwzu r7, 0x4 (r15)
mr r8, r18
lwzu r9, 0x4 (r15)
lwzu r10, 0x4 (r15)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
#Update the %1d's
addi r17, r17, 2
addi r18, r18, 2
subic. r16, r16, 1
bne+ fpr_sprintf_loop
#=======================
#sprint for first 'half' of misc registers
misc_title_string_offset = misc_start_string - table_start
add r3, r27, r28 #Keep updating dump spot
addi r4, r31, misc_title_string_offset
lwz r5, EVA_MISC (r29) #EVA_LR
lwz r6, EVA_CTR (r29)
lwz r7, EVA_CR (r29)
lwz r8, EVA_srr1 (r29)
lwz r9, EVA_XER (r29)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
#=======================
#final sprintf, the second 'half' of misc registers
misc_final_string_offset = misc_final_string - table_start
add r3, r27, r28 #Keep updating dump spot
addi r4, r31, misc_final_string_offset
lwz r5, EVA_FPSCR (r29)
lwz r6, EVA_HID0 (r29)
lwz r7, EVA_HID2 (r29)
lwz r8, EVA_HID4 (r29)
lwz r9, EVA_L2CR (r29)
mtctr r14
bctrl
cmpwi r3, 0
blt- HUGE_ERROR
#Update total bytes of formatted string for later use of ISFS_Write
add r27, r27, r3
#r27 now contains grant total of byte lenght of entire formatted string!
#========================
#Create report.txt (ios_ioctl using /dev/fs fd)
#IOS_Ioctl createFile structure (size 0x4C)
#0x0 (word) = Owner ID
#0x4 (halfword) = Group ID
#0x6 thru 0x45 = file path/name ; it's last byte must be null
#0x46 = Owner Permissions
#0x47 = Group Permissions
#0x48 = Other Permissions
#0x49 = u8 attributes
#0x4A & 0x4B = ??? (always appears to be null)
#Setup the IOCTL structure
#Transfer file name to it
#set all other value
#80002080 is where the structure is at
#First, null it all out
li r0, 0x13 #0x4C bytes is 0x13 (19) words
mtctr r0
li r0, 0
ori r3, r29, 0x207C
null_loop_ioctl:
stwu r0, 0x4 (r3)
bdnz+ null_loop_ioctl
#Transfer file name
file_path_offset = file_path - table_start
addi r3, r31, file_path_offset-4 #Need minus four for first iternation of loop's lwzu instruction
ori r4, r29, 0x2082 #-4 for stwu
#Lenght of file name/path (includes null byte at end) is exactly 5 words
li r0, 5
mtctr r0
file_name_transfer:
lwzu r0, 0x4 (r3)
stwu r0, 0x4 (r4)
bdnz+ file_name_transfer
#Complete rest of ioctl structure
li r0, 3
stb r0, 0x20C6 (r29)
stb r0, 0x20C7 (r29)
stb r0, 0x20C8 (r29)
#Note: There should be no need to null out the ios_structure. Universal ios places all the values in that structure, any unused values shouldn't have be null before any IPC fiddling, and the second half 0x20 bytes of the structure is completely ignored by IOS. Only putting this note here, just in case I am wrong and in the future ppl encounter bugs in this code, this may be the reason.
#=======================================
#Create the File!
#r3 = universal ios command
#r4 = 32 byte aligned pointer to the ios_structure
#r5 = fd of /dev/fs; always 2
#r6 = ioctl no. ; this is 9
#r7 = pointer to input buffer; must be 32 byte aligned, this is NOT the pointer to the ios_structure
#r8 = size of inpute buffer; 0x4C
li r3, 6 #ioctl command for universal ios
ori r4, r29, 0x2020
li r5, 2 #fd for /dev/fs, it's the same for every wii game
li r6, 9
ori r7, r29, 0x2080
li r8, 0x4C
bl universal_ios_function
cmpwi r3, 0
blt- HUGE_ERROR
#Open report.txt
#r3 = universal ios command
#r4 = 32 byte aligned pointer to the ios_structure
#r5 = pointer to file path
#r6 = permissions
li r3, 1
ori r4, r29, 0x2020
addi r5, r31, file_path_offset
li r6, 2
bl universal_ios_function
cmpwi r3, 0
blt- HUGE_ERROR
#Write to report.txt
#r3 = universal ios command
#r4 = 32 byte aligned pointer to the ios_structure
#r5 = fd
#r6 = pointer to stuff that will be written; must be 32-byte aligned
#r7 = amount of bytes to write
mr r26, r3 #Backup the returned fd for ISFS_Close afterwards
mr r5, r3 #fd needs to be in r5 for universal ios
li r3, 4 #ios_write for universal ios
ori r4, r29, 0x2020
mr r6, r28 #Backed up from earlier for sprintf r3 arg
mr r7, r27 #Backed up from earlier; return values added together from both sprintf calls
bl universal_ios_function
cmpwi r3, 0
blt- HUGE_ERROR
#Close report.txt
#r3 = universal ios command
#r4 = 32 byte aligned pointer to the ios_structure
#r5 = fd
li r3, 2 #ios_close for universal ios
ori r4, r29, 0x2020 #pointer to ios_structure; 32-byte aligned
mr r5, r26 #fd
bl universal_ios_function
cmpwi r3, 0
blt- HUGE_ERROR
#============================
#THE END!!!!
#We were sucessful, make the screen green
#Set green color (0,0)
lis r3, 0xCD80
lis r0, 0x0000 #li r0, 0 will work too, fyi
b update_vi
#Couldn't find sprintf or an ios error occurred, set screen to yellow
HUGE_ERROR:
lis r3, 0xCD80
lis r0, 0x00FF
update_vi:
ori r0, r0, 0xFF01 #Full brightness and flip enable bit high
stw r0, 0x0024 (r3)
loop_forever:
sync
nop
b loop_forever
#===========================
#Universal IOS; slightly modified, certain things not needed were commented out
#Start Assembly
#Prologue
universal_ios_function:
stwu sp, -0x0010 (sp)
mflr r0
stw r0, 0x0014 (sp)
stmw r30, 0x8 (sp)
#Disable External Interrupts; NOT NEEDED, interrupts already been disabled
#mfmsr r0
#rlwinm r11, r0, 0, 17, 15 #Flip off 'Allow interrupts' bit
#mtmsr r11 #Update MSR
#rlwinm r12, r0, 17, 31, 31 #Backup modified MSR state
#Backup 1st two Args
mr r31, r3
mr r30, r4
#r5 thru r8 remain untouched before final subroutine involving IPC, no need to back up them
#r9 and r10 remain untouched the entire time, no need to back them up
#Determine what IOS command is being utilized
cmpwi r31, 1
beq- universal_open
cmpwi r31, 2
beq- universal_close
#cmpwi r31, 5 #NOT NEEDED
#beq- universal_seek
cmpwi r31, 6
beq- universal_ioctl
#cmpwi r31, 7 #NOT NEEDED
#beq- universal_ioctlv
#Read/Write is being used; Note, only write is being used
#universal_readwrite: #the check below for read vs write not needed
#r5 = fd
#r6 = Input/Output Buffer
#r7 = Size
#Check if read (invalidate) or write (flush), adjust cache accordingly for IOS's 2nd arg
mr r3, r6 #still need these two instructions for the r3 and r4 arg setup for the flush
mr r4, r7
#cmpwi r31, 3 #Checks not needed!
#beq- its_read
#IOS_Write is being used, flush the input buffer
bl data_cache_flush
#b done_with_cache #With the read stuff being commented, no point having this as it just branches to very next instruction
#IOS_Read is being used, invalidate the output buffer; NOT NEEDED!!!!!!!!!!!!!!!!
#its_read:
#bl data_cache_invalidate
#Store fd, and other args to IOS structure; any mem pointer that is sent will be sent physically
#done_with_cache: #label name NOT needed
stw r5, 0x8 (r30)
clrlwi r0, r6, 1
stw r0, 0xC (r30)
stw r7, 0x10 (r30)
#Prep IOS structure, run IPC
b flush_ios_structure_then_run_ipc
#IOS_Open
universal_open:
#r5 = Pointer
#r6 = Permissions
#Figure out file path byte length
addi r3, r5, -1
li r4, 0 #Use r3 for counter for upcoming subroutine call (has 1 arg)
file_path_counter:
addi r4, r4, 1
lbzu r0, 0x1 (r3)
cmpwi r0, 0
bne+ file_path_counter
#Flush file path; r4 already set
mr r3, r5
bl data_cache_flush
#Store args to IOS structure; any mem pointer that is sent will be sent physically
clrlwi r0, r5, 1
stw r0, 0xC (r30) #No fd to store at 0x8
stw r6, 0x10 (r30)
#Prep IOS structure, run IPC
b flush_ios_structure_then_run_ipc
#IOS_Close
universal_close:
#r5 = fd
#Store fd to IOS structure
stw r5, 0x8 (r30)
#Prep IOS structure, run IPC
b flush_ios_structure_then_run_ipc
#IOS_Seek; NOT NEEDED!!!!!!!!!!!!!!!!!!!!!
#universal_seek:
#r5 = fd
#r6 = Offset from Location Mode
#r7 = Location Mode
#Store fd and other args to IOS structure
#stw r5, 0x8 (r30)
#stw r6, 0xC (r30)
#stw r7, 0x10 (r30)
#b flush_ios_structure_then_run_ipc
#IOS_Ioctl
universal_ioctl:
#r5 = fd
#r6 = ioctl_no
#r7 = inbuf
#r8 = size of inbuf
#r9 = outbuf
#r10 = size of outbuf
#Flush Input Buffer
mr r3, r7
mr r4, r8
bl data_cache_flush
#Invalidate Output Buffer; NOT NEEDED!!!!!!!!!!!! our ioctl for createfile has no output buffer!
#mr r3, r9
#mr r4, r10
#bl data_cache_invalidate
#Store fd and other args to IOS structure; any mem pointer that is sent will be sent physically
stw r5, 0x8 (r30)
stw r6, 0xC (r30)
clrlwi r0, r7, 1
stw r0, 0x10 (r30)
stw r8, 0x14 (r30)
clrlwi r0, r9, 1
stw r0, 0x18 (r30)
stw r10, 0x1C (r30)
b flush_ios_structure_then_run_ipc
#IOS_Ioctlv; NOT NEEDED!!!!!!!!!!!!!!!!!!!!!!!!!!
#universal_ioctlv:
#r5 = fd
#r6 = ioctl_no
#r7 = number of input buffers
#r8 = number of output buffers
#r9 = pointer to Vector table
#Flush entire Vector Table
#mr r3, r9
#add r4, r7, r8 #Input + Output Buffers
#bl data_cache_flush
#Store fd and other args to IOS structure; any mem pointer that is sent will be sent physically
#stw r5, 0x8 (r30)
#stw r6, 0xC (r30)
#stw r7, 0x10 (r30)
#stw r8, 0x14 (r30)
#clrlwi r0, r9, 1
#stw r0, 0x18 (r30)
#b flush_ios_structure_then_run_ipc
#Subroutine for data cache flush
data_cache_flush:
rlwinm r11, r3, 0, 27, 31
add r4, r4, r11
addi r4, r4, 31
rlwinm r0, r4, 27, 5, 31
mtctr r0
flush_loop:
dcbf 0, r3
addi r3, r3, 32 #Move on to next cache block (each block is 0x20 in size)
bdnz+ flush_loop
sc #Update HID0
blr
#Subroutine for data cache invalidate
data_cache_invalidate:
rlwinm r11, r3, 0, 27, 31
add r4, r4, r11
addi r4, r4, 31
rlwinm r0, r4, 27, 5, 31
mtctr r0
inval_loop:
dcbi 0, r3
addi r3, r3, 32 #Move on to next cache block (each block is 0x20 in size)
bdnz+ inval_loop
blr
#Flush first 0x20 bytes of IOS structure
flush_ios_structure_then_run_ipc:
stw r31, 0 (r30) #Store IOS command
mr r3, r30
li r4, 0x20
bl data_cache_flush
#Run the IPC Engine
#Set Hollywood IPC/IRQ upper 16 bits
lis r6, 0xCD00 #You think this would be 0xCD80, but this is how Wii games do it.
#Send IOS structure pointer (physical) to IPC_PPCMSG
clrlwi r0, r30, 1
stw r0, 0 (r6)
#Tell IPC_PPCCTRL to execute command; do NOT modify the IY1 and IY2 bits.
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0001
stw r0, 0x4 (r6)
#Wait for Starlet to acknowledge our command
check_for_acknowledge:
lwz r0, 0x4 (r6)
andi. r0, r0, 0x22 #Check if IY2 and Y2 bits were flipped high by Starlet
cmpwi r0, 0x22
bne- check_for_acknowledge
#Starlet has acknowledged, rewrite Acknowledge bit flipped high to clear it
#Then disable IRQ-30, and now wait for Starlet to execute the command & have pointer (physical IOS structure) avialable in IPC_ARMMSG
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0002
stw r0, 0x4 (r6)
lis r5, 0x4000 #Keep value in r5 for later secondary use
stw r5, 0x30 (r6)
check_if_executed:
lwz r0, 0x4 (r6)
andi. r0, r0, 0x14
cmpwi r0, 0x14
bne- check_if_executed
#Check if pointer is available in IPC_ARMMSG
lwz r4, 0x8 (r6) #Using r4 due to upcoming addis instruction
cmpwi r4, 0
li r3, -5
beq- restore_interrupts #No pointer, somehow a different IPC call was being done during our work?
#Turn IOS structure pointer back to virtual, then invalidate the first 0x20 bytes
addis r3, r4, 0x8000 #Could also just use mr r4, r30 here, w/e works
li r4, 0x20
bl data_cache_invalidate
#Starlet has executed our command, rewrite Execute bit flipped high to clear it
#Then disable IRQ-30 one more time, is is needed or else next IOS call keep IPC_PPCCTRL at 0x31 forever...
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0004
stw r0, 0x4 (r6)
stw r5, 0x30 (r6)
#Set IPC_PPCMSG X bits to relaunch
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0008
stw r0, 0x4 (r6)
#Put return value into r3
lwz r3, 0x4 (r30)
#Restore External Interrupts; NOT NEEDED!!!! leave the branch label destination though
restore_interrupts:
#cmpwi r12, 0
#mfmsr r4
#beq- clear_bit
#ori r0, r4, 0x8000 #Flip on 'Allow Interrupts' bit
#b skip_clear_bit
#clear_bit:
#rlwinm r0, r4, 0, 17, 15
#skip_clear_bit:
#mtmsr r0
#Epilogue; r3 contains return value
epilogue:
lmw r30, 0x8 (sp)
lwz r0, 0x0014 (sp)
mtlr r0
addi sp, sp, 0x0010
blr
#
#########################################
the_entire_code:
mflr r11
#Store physical pointer in sprg3, no standard vector exceptions use this spare register, and afaik there's no reason for any game to use this register in any non-exception use.
clrlwi r11, r11, 1
mtsprg3 r11
#Recover C0 LR
mtlr r0
#blr #end C0
#END EVERYTHING!