• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
March 28, 2024, 12:33:34 pm

News:

Don't be hasty to start your own mod; all our FFT modding projects are greatly understaffed! Find out how you can help in the Recruitment section or our Discord!


Let's try to fix WotL's slowdown! Now with real progress.

Started by endrift, February 12, 2012, 07:46:57 pm

endrift

Slowdown patches can be found here: http://ffhacktics.com/smf/index.php?topic=8490.0

Old post:

For the past week or so, I've had my PSP strapped into a debugger and have been slowly picking apart the assembly of War of the Lions and documenting it by transforming it into semi-readable C code. In order to stave off potential copyright concerns, I will NOT be posting straight dumps or disassemblies, although I can provide instructions for obtaining these manually from your own copies of WotL. (It's pretty easy, assuming you have custom firmware. Without custom firmware, it's a bit more difficult.) While this will complicate visualizing the mapping of C to and from assembly, as I won't be able to post my annotated disassemblies, I hope that that won't impede progress too much, and I do encourage people to try and do some of this work in parallel to make sure that I don't make too many mistakes while sorting through the myriad lines of disassembled code.

I will be keeping a git repository containing all of the progress (that I can post, as discussed above) on BitBucket: https://bitbucket.org/Archaemic/wotl

There is a central notes file called wotlnotes.txt, while most of the code I will be tossing into src/wotl.c. If any of these grows too large, I will split it up. I will also try to keep an up-to-date version of wotlnotes.txt in the following post. (I'm making it a separate post because it will be BIG.)

Here is a (hopefully) up to date list of what's going on:

What this means:

  • I will be making an effort to fix the slowdown and, if possible, sound desync issues.

  • It's not going to be easy; it will take me a while to get anywhere significant.

  • I can't guarantee I will actually get anywhere significant!

  • I'm posting almost everything online, so other people can help if they want.

  • Until then, I'm basically working alone. If you have experience and want to step up to the plate, you are welcome to!



What I have done so far:

  • Created a framework for attaching hooks into system calls, allowing me to trace where things get called.

  • Found out where things hook into the PSP's graphics engine.

  • Reverse engineered some parts of War of the Lion's graphics engine.

  • Unstretched the screen (in most places).

  • Figured out the general structure of a single redraw.

  • Found what is causing the slowdown.

  • Nullified the slowdown.



What I am working on right now:

  • Seeing if nullifying the slowdown has ill effects, and if so, what the best way to manage this is.

  • Discerning why the slowdown exists.

  • Locating where instructions are written from each frame.

  • Testing a screen unstretching patch.

  • Finding more places where the screen gets stretched.

  • Figuring out why there are PS1 GPU calls in a PSP game.

  • Figuring out where the halving and thirding are occurring.



What I have NOT done so far:

  • Figured out why the results of the timing function are having the affect they are.

  • Figured out the best way to remove the slowdown.

  • Found out where the GE lists are initialized.

  • Found out where the PS1 GPU calls are going.

  • Found out if the graphics engine is actually PS1 GPU emulation.

  • Found out what's causing any of the slowdown.

  • Fixed any of the slowdown.



Some miscellaneous observations:

  • The entirety of the main executable from the JP PS1 version of FFT is present in the file fftpack.bin. I can't find any traces of this file in the game when it's running, though.

  • The main thread for the game is called "psx_main", entailing that there is emulation going on. Given that the PS-X EXE doesn't appear to be in memory, though, this implies that there either isn't actually emulation, or they're doing JITing/dynarec for some reason. (I'd imagine all you'd need to fix is the memory accesses and offsets, and hardware I/O, but I might be wrong. That can be done easily with dynarec though.)

  • Very few GPU/ge calls are made per frame, seemingly always 0 (when the screen is blank) to 6. 2-4 seems typical. This implies that most of the rendering is either done all at once by sending a ton of commands to the GPU (which seems to be the case, given the mass of kilobytes of GPU instructions floating around at around 0x90f3c80), or done in CPU (would explain some of the slowdown!).

  • GPU calls seem to only have about 16 instructions at once (although I haven't confirmed this), meaning that doing all the rendering on the GPU in spurts is even more unlikely. This was a mistake. There are actually tons of instructions passed at a time. The 16 referred to the size of something else.

  • Frames are usually only drawn once every 1 or 2 vblanks, depending on factors that I am not sure of. For example, the load from memory stick screen is usually 1 redraw every two vblanks, but a lot of other stuff is every vblank.

  • During casting, the game only redraws every third vblank. I'm still looking into why.

  • I've found code that talks to the GPU, but modifying it doesn't seem to make the GPU do anything differently. Hurm.... Must investigate further.

  • There are PS1 GPU calls in the game that actually have an effect. It looks like there might be PS1 GPU emulation here.

  • The game rewrites all of its graphics instructions (up to 512kB) every frame. This might be related to the slowdown--it might try to do one (or two) frame(s) to write the instructions, one frame to run the instructions.

  • The slowdown is caused by something going funky in the function that detects timing between frames. Setting a fixed value for the time it tells us removes the slowdown, but I'm not sure why yet.

  • Modding version: Other/Unknown

endrift

February 12, 2012, 07:47:39 pm #1 Last Edit: February 24, 2012, 12:34:20 am by Archaemic
wotlnotes.txt:
SYSDIR/BOOT.BIN:
===
Add 0x8803fac for offsets in memory

0x00000054: .text (mapped to 0x08804000 in RAM, entry at 0x08814b00)
0x002479a8: .rodata.sceNid (mapped to 0x08a4b954 in RAM)
0x0015fe14: .data (mapped to 0x08a4c11c in RAM)
0x0026f930: Dimensions of battle screen
0x002793cc: Dimensions of world map when returning from chronicle screen
0x0027a1d4: Dimensions of world map when returning from tutorial screen

Set dimensions to 00 00 00 00 e0 01 10 01 46 00 10 00 e0 01 10 01 to disable stretch and center screen

USRDIR/fftpack.bin:
===
ASM block at 0x00009a30 - 0x00021604
ASM block at 0x00039448 - 0x0003d458
ASM block at 0x0004f8a4 - 0x00054948
ASM block at 0x0005dbc0 - 0x00089438
ASM block at 0x000dca4c (?) - 0x000eb28c
ASM block at 0x001223f8 - 0x00141e1c
ASM block at 0x00167b3c - 0x00182ca0
ASM block at 0x0018761c - 0x001a8058
ASM block at 0x001b2800 - 0x001b6c5c
ASM block at 0x001c001c - 0x001cc278
ASM block at 0x001df000 - 0x001e05d4
ASM block at 0x001f1004 - 0x001f6ffc
ASM block at 0x00209004 - 0x00213f98
ASM block at 0x00228800 - 0x0022ea40
ASM block at 0x00238818 - 0x00249858
ASM block at 0x00263170 - 0x0026cfb8
ASM block at 0x0027e204 - 0x0028d880

SLPS-00770 system.cnf: 0x00008000 (not mapped)
SLPS-00770 PS-X EXE:   0x00008800 (not mapped)
psx_main thread entry: 0x002633d8 (mapped to 0x089efcc0)

In memory:
===
Syscall table (.sce.stubText): 0x08a4af14 - 0x08a4b4e8
Mem addr.  | Syscall ID  | NID        | Function name
-----------+-------------+------------+---------------------------------
0x08a4af14 | 0x207e      | 0xca04a2b9 | sceKernelRegisterSubIntrHandler
0x08a4af1c | 0x2076      | 0xd61e6961 | sceKernelReleaseSubIntrHandler
0x08a4af24 | 0x2078      | 0xfb8e22ec | sceKernelEnableSubIntr
0x08a4af2c | 0x2131      | 0x42ec03ac | sceIoWrite
0x08a4af34 | 0x2133      | 0x55f4717d | sceIoChdir
0x08a4af3c | 0x2137      | 0x6a638d83 | sceIoRead
0x08a4af44 | 0x213a      | 0x779103a0 | sceIoRename
0x08a4af4c | 0x213b      | 0x810c4bc3 | sceIoClose
0x08a4af54 | 0x213d      | 0xa0b5a7c2 | sceIoReadAsync
0x08a4af5c | 0x2142      | 0xb29ddf9c | sceIoDopen
0x08a4af64 | 0x214a      | 0xeb092469 | sceIoDclose
0x08a4af6c | 0x212b      | 0x109f50bc | sceIoOpen
0x08a4af74 | 0x212e      | 0x27eb27b8 | sceIoLseek
0x08a4af7c | 0x2130      | 0x35dbd746 | sceIoWaitAsyncCB
0x08a4af84 | j 0x8800080 | 0x092968f4 | sceKernelCpuSuspendIntr
0x08a4af8c | j 0x88000ac | 0x5f10d406 | sceKernelCpuResumeIntr
0x08a4af94 | 0x21ff      | 0x05572a5f | sceKernelExitGame
0x08a4af9c | 0x2202      | 0x4ac57943 | sceKernelRegisterExitCallback
0x08a4afa4 | 0x2159      | 0x2e0911aa | sceKernelUnloadModule
0x08a4afac | 0x214e      | 0xd8b73127 | sceKernelGetModuleIdByAddress
0x08a4afb4 | 0x2150      | 0xf0a26395 | sceKernelGetModuleId
0x08a4afbc | 0x215e      | 0x8f2df740 | ModuleMgrForUser_8F2DF740
0x08a4afc4 | 0x214b      | 0xd1ff982a | sceKernelStopModule
0x08a4afcc | 0x2118      | 0x172d316e | sceKernelStdin
0x08a4afd4 | 0x2120      | 0xa6bab2e9 | sceKernelStdout
0x08a4afdc | 0x2121      | 0xf78ba90a | sceKernelStderr
0x08a4afe4 | 0x21bb      | 0x13a5abef | sceKernelPrintf
0x08a4afec | 0x21bd      | 0x237dbd4f | sceKernelAllocPartitionMemory
0x08a4aff4 | 0x21a9      | 0x7591c7db | sceKernelSetCompiledSdkVersion
0x08a4affc | 0x21ad      | 0x9d9a5ba1 | sceKernelGetBlockHeadAddr
0x08a4b004 | 0x21b1      | 0xb6d61d02 | sceKernelFreePartitionMemory
0x08a4b00c | 0x21b5      | 0xf77d77cb | sceKernelSetCompilerVersion
0x08a4b014 | 0x2094      | 0xceadeb47 | sceKernelDelayThread
0x08a4b01c | 0x20bf      | 0x1fb15a32 | sceKernelSetEventFlag
0x08a4b024 | 0x2099      | 0xd6da4ba1 | sceKernelCreateSema
0x08a4b02c | 0x20a2      | 0xe81caf8f | sceKernelCreateCallback
0x08a4b034 | 0x20a6      | 0xed1410e0 | sceKernelDeleteFpl
0x08a4b03c | 0x20a8      | 0xef9e4c70 | sceKernelDeleteEventFlag
0x08a4b044 | 0x20a9      | 0xf0b7da1c | sceKernelDeleteMsgPipe
0x08a4b04c | 0x20ab      | 0xf475845d | sceKernelStartThread
0x08a4b054 | 0x20ac      | 0xf6414a71 | sceKernelFreeFpl
0x08a4b05c | 0x20c1      | 0x278c0df5 | sceKernelWaitThreadEnd
0x08a4b064 | 0x20c3      | 0x28b6489c | sceKernelDeleteSema
0x08a4b06c | 0x20c8      | 0x30fd48f0 | sceKernelPollEventFlag
0x08a4b074 | 0x20ca      | 0x328c546a | sceKernelWaitEventFlagCB
0x08a4b07c | 0x20ce      | 0x349b864d | sceKernelCancelMsgPipe
0x08a4b084 | 0x20d2      | 0x383f7bcc | sceKernelTerminateDeleteThread
0x08a4b08c | 0x20d6      | 0x3f53e640 | sceKernelSignalSema
0x08a4b094 | 0x20d7      | 0x402fcf22 | sceKernelWaitEventFlag
0x08a4b09c | 0x20d8      | 0x446d8de6 | sceKernelCreateThread
0x08a4b0a4 | 0x20db      | 0x4e3a1105 | sceKernelWaitSema
0x08a4b0ac | 0x20e0      | 0x55c20a00 | sceKernelCreateEventFlag
0x08a4b0b4 | 0x20e8      | 0x623ae665 | sceKernelTryAllocateFpl
0x08a4b0bc | 0x20ec      | 0x6652b8ca | sceKernelSetAlarm
0x08a4b0c4 | 0x20f6      | 0x74829b76 | sceKernelReceiveMsgPipe
0x08a4b0cc | 0x20f8      | 0x7c0dc2a0 | sceKernelCreateMsgPipe
0x08a4b0d4 | 0x20fb      | 0x7e65b999 | sceKernelCancelAlarm
0x08a4b0dc | 0x20fc      | 0x809ce29b | sceKernelExitDeleteThread
0x08a4b0e4 | 0x20fd      | 0x812346e4 | sceKernelClearEventFlag
0x08a4b0ec | 0x2101      | 0x82bc5777 | sceKernelGetSystemTimeWide
0x08a4b0f4 | 0x2108      | 0x884c9f90 | sceKernelTrySendMsgPipe
0x08a4b0fc | 0x210f      | 0x9944f31f | sceKernelSuspendThread
0x08a4b104 | 0x2111      | 0x9fa03cd3 | sceKernelDeleteThread
0x08a4b10c | 0x207f      | 0xaa73c935 | sceKernelExitThread
0x08a4b114 | 0x208e      | 0xc07bb470 | sceKernelCreateFpl
0x08a4b11c | 0x21cf      | 0x71ec4271 | sceKernelLibcGettimeofday
0x08a4b124 | 0x21d8      | 0x91e4f6a7 | sceKernelLibcClock
0x08a4b12c | 0x21ef      | 0x27cc57f0 | sceKernelLibcTime
0x08a4b134 | 0x21f4      | 0x3ee30821 | sceKernelDcacheWritebackRange
0x08a4b13c | 0x22e2      | 0x136caf51 | sceAudioOutputBlocking
0x08a4b144 | 0x22e3      | 0x13f592bc | sceAudioOutputPannedBlocking
0x08a4b14c | 0x22e9      | 0x5ec81c55 | sceAudioChReserve
0x08a4b154 | 0x22ed      | 0x6fc46853 | sceAudioChRelease
0x08a4b15c | 0x22f1      | 0x95fd0c2d | sceAudioChangeChannelConfig
0x08a4b164 | 0x22d5      | 0xb011922f | sceAudioGetChannelRestLength
0x08a4b16c | 0x22d6      | 0xb7e1d8e7 | sceAudioChangeChannelVolume
0x08a4b174 | 0x22d7      | 0xcb2e439e | sceAudioSetChannelDataLen
0x08a4b17c | 0x22d9      | 0xe2d56b2d | sceAudioOutputPanned
0x08a4b184 | 0x2285      | 0x1f4011e6 | sceCtrlSetSamplingMode
0x08a4b18c | 0x2289      | 0x3a622550 | sceCtrlPeekBufferPositive
0x08a4b194 | 0x228e      | 0x6a2774f3 | sceCtrlSetSamplingCycle
0x08a4b19c | 0x2273      | 0x0e20f177 | sceDisplaySetMode
0x08a4b1a4 | 0x2275      | 0x210eab3a | sceDisplayGetAccumulatedHcount
0x08a4b1ac | 0x2276      | 0x289d82fe | sceDisplaySetFrameBuf
0x08a4b1b4 | 0x227a      | 0x46f186c3 | sceDisplayWaitVblankStartCB
0x08a4b1bc | 0x2267      | 0x984c27e7 | sceDisplayWaitVblankStart
0x08a4b1c4 | 0x2268      | 0x9c6eaad7 | sceDisplayGetVcount
0x08a4b1cc | 0x2207      | 0x617f3fe6 | sceDmacMemcpy
0x08a4b1d4 | 0x220c      | 0x03444eb4 | sceGeListSync
0x08a4b1dc | 0x220d      | 0x05db22ce | sceGeUnsetCallback
0x08a4b1e4 | 0x220f      | 0x1c0d95a6 | sceGeListEnQueueHead
0x08a4b1ec | 0x2212      | 0x4c06e472 | sceGeContinue
0x08a4b1f4 | 0x2215      | 0xa4fc06a4 | sceGeSetCallback
0x08a4b1fc | 0x2216      | 0xab49e76a | sceGeListEnQueue
0x08a4b204 | 0x2217      | 0xb287bd61 | sceGeDrawSync
0x08a4b20c | 0x2218      | 0xb448ec0d | sceGeBreak
0x08a4b214 | 0x221b      | 0xe0d68148 | sceGeListUpdateStallAddr
0x08a4b21c | 0x221c      | 0xe47e40e4 | sceGeEdramGetAddr
0x08a4b224 | j 0x9c08e38 | 0x0e3c2e9d | sceMpegAvcDecode
0x08a4b22c | j 0x9c09868 | 0x13407f13 | sceMpegRingbufferDestruct
0x08a4b234 | j 0x9c08aa0 | 0x167afd9e | sceMpegInitAu
0x08a4b23c | j 0x9c08508 | 0x21ff80e4 | sceMpegQueryStreamOffset
0x08a4b244 | j 0x9c097d0 | 0x37295ed8 | sceMpegRingbufferConstruct
0x08a4b24c | j 0x9c08878 | 0x42560f23 | sceMpegRegistStream
0x08a4b254 | j 0x9c09050 | 0x4571cc64 | sceMpegAvcDecodeFlush
0x08a4b25c | j 0x9c088dc | 0x591a4aa2 | sceMpegUnRegistStream
0x08a4b264 | j 0x9c08830 | 0x606a4649 | sceMpegDelete
0x08a4b26c | j 0x9c08570 | 0x611e9e11 | sceMpegQueryStreamSize
0x08a4b274 | j 0x9c085c8 | 0x682a619b | sceMpegInit
0x08a4b27c | j 0x9c08df0 | 0x707b7629 | sceMpegFlushAllStream
0x08a4b284 | j 0x9c08fd8 | 0x740fccd1 | sceMpegAvcDecodeStop
0x08a4b28c | j 0x9c093f0 | 0x800c44df | sceMpegAtracDecode
0x08a4b294 | j 0x9c08604 | 0x874624d6 | sceMpegFinish
0x08a4b29c | j 0x9c08934 | 0xa780cf7e | sceMpegMallocAvcEsBuf
0x08a4b2a4 | j 0x9c098b0 | 0xb240a59e | sceMpegRingbufferPut
0x08a4b2ac | j 0x9c09918 | 0xb5f6dc87 | sceMpegRingbufferAvailableSize
0x08a4b2b4 | j 0x9c08640 | 0xc132e22f | sceMpegQueryMemSize
0x08a4b2bc | j 0x9c08978 | 0xceb870b1 | sceMpegFreeAvcEsBuf
0x08a4b2c4 | j 0x9c09740 | 0xd7a29f46 | sceMpegRingbufferQueryMemSize
0x08a4b2cc | j 0x9c086d8 | 0xd8c5f121 | sceMpegCreate
0x08a4b2d4 | j 0x9c08d20 | 0xe1ce83a7 | sceMpegGetAtracAu
0x08a4b2dc | j 0x9c089d0 | 0xf8dcb679 | sceMpegQueryAtracEsSize
0x08a4b2e4 | j 0x9c08c30 | 0xfe246728 | sceMpegGetAvcAu
0x08a4b2ec | 0x15        | 0x157e6225 | sceNetAdhocPtpClose
0x08a4b2f4 | 0x15        | 0x4da4c788 | sceNetAdhocPtpSend
0x08a4b2fc | 0x15        | 0x877f6d66 | sceNetAdhocPtpOpen
0x08a4b304 | 0x15        | 0x8bea2b3e | sceNetAdhocPtpRecv
0x08a4b30c | 0x15        | 0x9ac2eeac | sceNetAdhocPtpFlush
0x08a4b314 | 0x15        | 0x9df81198 | sceNetAdhocPtpAccept
0x08a4b31c | 0x15        | 0xa62c6f57 | sceNetAdhocTerm
0x08a4b324 | 0x15        | 0xe08bdac1 | sceNetAdhocPtpListen
0x08a4b32c | 0x15        | 0xe1d621d7 | sceNetAdhocInit
0x08a4b334 | 0x15        | 0xfc6fc07b | sceNetAdhocPtpConnect
0x08a4b33c | 0x15        | 0x08fff7a0 | sceNetAdhocctlScan
0x08a4b344 | 0x15        | 0x20b317a0 | sceNetAdhocctlAddHandler
0x08a4b34c | 0x15        | 0x34401d65 | sceNetAdhocctlDisconnect
0x08a4b354 | 0x15        | 0x5e7f79c9 | sceNetAdhocctlJoin
0x08a4b35c | 0x15        | 0x6402490b | sceNetAdhocctlDelHandler
0x08a4b364 | 0x15        | 0x81aee1be | sceNetAdhocctlGetScanInfo
0x08a4b36c | 0x15        | 0x9d689e13 | sceNetAdhocctlTerm
0x08a4b374 | 0x15        | 0xe162cb14 | sceNetAdhocctlGetPeerList
0x08a4b37c | 0x15        | 0xe26f226e | sceNetAdhocctlInit
0x08a4b384 | 0x15        | 0xec0635c1 | sceNetAdhocctlCreate
0x08a4b38c | 0x15        | 0x0bf0a3ae | sceNetGetLocalEtherAddr
0x08a4b394 | 0x15        | 0x281928a9 | sceNetTerm
0x08a4b39c | 0x15        | 0x39af39a6 | sceNetInit
0x08a4b3a4 | 0x15        | 0x89360950 | sceNetEtherNtostr
0x08a4b3ac | 0x244e      | 0x019b25eb | __sceSasSetADSR
0x08a4b3b4 | 0x2450      | 0x267a6dd2 | __sceSasRevParam
0x08a4b3bc | 0x2451      | 0x2c8e6ab3 | __sceSasGetPauseFlag
0x08a4b3c4 | 0x2452      | 0x33d4ab37 | __sceSasRevType
0x08a4b3cc | 0x2453      | 0x42778a9f | __sceSasInit
0x08a4b3d4 | 0x2454      | 0x440ca7d8 | __sceSasSetVolume
0x08a4b3dc | 0x2456      | 0x50a14dfc | __sceSasCoreWithMix
0x08a4b3e4 | 0x2457      | 0x5f9529f6 | __sceSasSetSL
0x08a4b3ec | 0x2458      | 0x68a46b95 | __sceSasGetEndFlag
0x08a4b3f4 | 0x245a      | 0x74ae582a | __sceSasGetEnvelopeHeight
0x08a4b3fc | 0x245b      | 0x76f01aca | __sceSasSetKeyOn
0x08a4b404 | 0x245c      | 0x787d04d5 | __sceSasSetPause
0x08a4b40c | 0x245d      | 0x99944089 | __sceSasSetVoice
0x08a4b414 | 0x245e      | 0x9ec3676a | __sceSasSetADSRmode
0x08a4b41c | 0x245f      | 0xa0cf2fa4 | __sceSasSetKeyOff
0x08a4b424 | 0x2461      | 0xa3589d81 | __sceSasCore
0x08a4b42c | 0x2462      | 0xad84d37f | __sceSasSetPitch
0x08a4b434 | 0x2463      | 0xb7660a23 | __sceSasSetNoise
0x08a4b43c | 0x2442      | 0xbd11b7c2 | __sceSasGetGrain
0x08a4b444 | 0x2443      | 0xcbcd4f79 | __sceSasSetSimpleADSR
0x08a4b44c | 0x2444      | 0xd1e0a01e | __sceSasSetGrain
0x08a4b454 | 0x2445      | 0xd5a229c9 | __sceSasRevEVOL
0x08a4b45c | 0x2447      | 0xe175ef66 | __sceSasGetOutputmode
0x08a4b464 | 0x2449      | 0xe855bf76 | __sceSasSetOutputmode
0x08a4b46c | 0x244b      | 0xf983b186 | __sceSasRevVON
0x08a4b474 | 0x21c5      | 0x090ccb3f | sceKernelPowerTick
0x08a4b47c | 0x2260      | 0x4a9e5e29 | sceUmdWaitDriveStatCB
0x08a4b484 | 0x2262      | 0x6af9b50a | sceUmdCancelWaitDriveStat
0x08a4b48c | 0x2263      | 0x6b4a146c | sceUmdGetDriveStat
0x08a4b494 | 0x2253      | 0x8ef08fce | sceUmdWaitDriveStat
0x08a4b49c | 0x2257      | 0xc6183d47 | sceUmdActivate
0x08a4b4a4 | 0x2259      | 0xe83742ba | sceUmdDeactivate
0x08a4b4ac | 0x2378      | 0x2a2b3de0 | sceUtilityLoadModule
0x08a4b4b4 | 0x238d      | 0x50c4cd57 | sceUtilitySavedataInitStart
0x08a4b4bc | 0x239b      | 0x8874dbe0 | sceUtilitySavedataGetStatus
0x08a4b4c4 | 0x23a2      | 0x9790b33c | sceUtilitySavedataShutdownStart
0x08a4b4cc | 0x23b9      | 0xd4b95ffb | sceUtilitySavedataUpdate
0x08a4b4d4 | 0x23c0      | 0xe49bfe92 | sceUtilityUnloadModule
0x08a4b4dc | 0x2320      | 0x0c622081 | sceWlanGetEtherAddr
0x08a4b4e4 | 0x241e      | 0x36aa6e91 | sceImposeSetLanguageMode

Near $gp:
/* Final screen texture x */
unsigned short textureX; // 0x8bae180

/* Final screen texture y */
unsigned short textureY; // 0x8bae182

/* Final screen texture width */
unsigned short textureWidth; // 0x8bae184

/* Final screen texture height */
unsigned short textureHeight; // 0x8bae186

/* Final screen polygon x */
unsigned short polyX; // 0x8bae188

/* Final screen polygon y */
unsigned short polyY; // 0x8bae18a

/* Final screen polygon width */
unsigned short polyWidth; // 0x8bae18c

/* Final screen polygon height */
unsigned short polyHeight; // 0x8bae18e

Map of .bss:
...
// 0x8baf764 : numeric or logical
...
// 0x8baf76c : size is 0x72 + 0xFC * 5
struct WotlDrawContext {
// 0x8baf76c : numeric or logical (can be -1)

// 0x8baf770 : numeric or logical
...
/* -1 to 4, index of active WotlGeContext */
int activeIndex; // 0x8baf778

// 0x8baf77c : tested against 1, but also used as a pointer; same type as 0x8baf7b8
...
/* Queue ID from sceGeListEnQueue, set in func_88047bc */
int qid0; // 0x8baf784

/* Queue ID from sceGeListEnQueue, set in func_8804d30 */
int qid1; // 0x8baf788

/* Callback ID for sceGeListEnQueue */
int cbid; // 0x8baf78c

/* Framebuffer pixel store mode */
int fbPsm; // 0x8baf790

/* Framebuffer width */
int fbWidth; // 0x8baf794

/* Pointer to framebuffer */
void* framebuffer; // 0x8baf798;
...
/* Viewport width */
int vpWidth; // 0x8baf7a8

/* Viewport height */
int vpHeight; // 0x8baf7ac

// 0x8baf7b0 : int

WotlGeContext* activeGeContext; // 0x8baf7b4

// 0x8baf7b8 : size is 0xFC * 5
struct WotlGeContext {
// 0x8baf7b8 : numeric (?)

/* Pointer to the GE list */
void* list; // 0x8baf7bc

/* Pointer to the GE stall list */
void* stall; // 0x8baf7c0

/* Size of the GE list */
int size; 0x8baf7c4

/* Index into the following array */
int index; // 0x8baf7c8
...
// 0x8baf7cc : array of pointer
...
/* Framebuffer pixel store mode */
int fbPsm; // 0x8baf850
...
/* Viewport width */
int vpWidth; // 0x8baf858

/* Viewport height */
int vpHeight; // 0x8baf85c
...
// 0x8baf8b0 : -1 to 4?
} geContext[5];
}

psx_main thread entry: 0x089efcc0
global pointer: 0x08bb3ea0
GE instruction lists:
0x90f2c80 - 0x90f3c80: copy screen buffer
0x90f3c80 - 0x9173c80: redraw scene

ACTIONS
===
To disable stretch:
pokeh 0x08bae18a 16
pokeh 0x08bae188 70
pokeh 0x08bae186 0x110
pokeh 0x08bae184 0x1e0

ARCHITECTURE
===
There are two GE lists. One is at 0x90f2c80. This list stays mostly static and is used for copying the screen buffer
onto the screen. The second is at 0x90f3c80, and contains all of the instructions used for redrawing the screen. Note
that pointers into the information referenced within the instructions (like the vertex buffers) point to addresses
within the lists. These are skipped using JUMPs.

The GE lists are rewritten in their entireties every frame. Note that nuances may change between frames, like vertex
locations. I am still investigating where this gets rewritten.

There are 3 signals used for GE calls:
- 1192: Redrawing the back buffer (scene)
- 1919: Copying the back buffer to the main screen
- 2005: Unknown

There are 5 GE contexts (WotlGeContext). Each of them corresponds to a different operation:
- 0: Drawing the back buffer onto the current frame
- 1: ???
- 2: Drawing the current scene
- 3: ???
- 4: ???

MISCELLANEOUS
===
The PSP (MIPS32R2 Allegrex) calling convention seems to dictate that args that don't fit into $a* go into $t*. Beware!

Evidence in the function at 0x88350c0 suggests that the game is still using the PS1's graphics protocol, emulated onto
the PSP. Yikes.

I found some dead code that seems to access 0x1f80xxxx, which are related to the PS1's scratchpad, but none of these
accesses are ever actually made. No references to PS1's GPU registers appear to be made.

Code that writes to the stall list seems to start at 0x880513c and continue until perhaps 0x8806cd8. These are probably
the guts of the graphics routines. Look for lui *, 0x8bb followed by lw *, -2124(*).

The slowdown is directly affected by the return value of the function at 0x882b800.

GLOSSARY
===
GE: Presumably stands for "graphics engine". It's the graphics API on the PSP, and works by sending a list of assembled
    instructions in the GE's native format to the GPU all at once. There is no high-level abstraction of the
    instructions used in War of the Lions: they assemble the instructions by hand.

GE list: A list of instructions that are to be fed to the PSP's GPU. The instructions themselves are 4 bytes per
    instruction and documentation of the instructions can be found in the PSPSDK at src/gu/doc/commands.txt.

NID: An identifier that maps to a kernel system call on the PSP. Instead of using a straightforward constant syscall
    table like a sane operating system like Linux, they use NIDs to refer to syscalls, and then syscall table is loaded
    into the .sceStub.text section.

stall address: The address at which a GE list will stop executing until another list is enqueued or the stall address
    is updated to be further into the list.

SCRATCH
===
These are miscellaneous notes for what I'm working on at the moment

sceGe* called from:
(during MPEG playback)
0x08804d94 -> sceGeListEnQueue
(during frame)
0x0883578c -> sceGeEdramGetAddr
0x08807efc -> sceGeListUpdateStallAddr
0x08807efc
0x08804d94 -> sceGeListEnQueue
called from 0x08835390 -> 0x08804d30
0x08805724 -> sceGeListSync
0x08804988 -> sceGeListEnQueue

Look into these calls:
$ra: 0x08835300
$a0: 0x00000002
$a1: 0x090f3c80
$a2: 0x00080000

$ra: 0x088338ac
$a0: 0x00000000
$a1: 0x090f2c80
$a2: 0x00001000

There is a large struct (at least 108 bytes) that starts at 0x974ba80

Unstretch queue (calls to 0x8a08b80):
[x] 0x88d84a4 (new game prologue screen)
[-] 0x88d8308 (video cutscene, not stretched?)
[x] 0x887503c (battle map)
[?] 0x89aa3b4 (save screen)
[ ] 0x89aa938 (???)
[ ] 0x8a0b14c (pre-character info screen?, pre-world map?)
[x] 0x897f800 (character info screen)
[ ] 0x897ff80 (post-character info screen?)
[x] 0x8a0ab38 (data screen)
[ ] 0x88f806c (pre-world map?)
[x] 0x88f7ed8 (world map)
[ ] 0x8882ccc (after death before title screen?)
[ ] 0x88826e0 (title screen?)
[x] 0x88e3368 (return from chronicle screen)
[x] 0x890d61c (return from tutorial screen)

RE queue:
0x882b800 (calls weird graphics functions, affects timing)
0x882c1e0 (calls 0x882d600)
0x882d600 (called every frame, write a decompiler!)
0x8835798, 0x882eab0, 0x882e880, 0x883165c
0x8805518, 0x8805690, 0x8849540 (inserts JUMPs)
0x8804f78, 0x8807db0 (might have something to do with initializing the GE contexts)
0x882c280 (might be what executes PS1 instructions)
0x8835f00 (GE signal handler)
0x8835f80 (install GE signal handler)
0x8a08d80, 0x8a08cc0 (used for storing dimensions)
0x8a0aac0, 0x88d8180
0x88358c0, 0x88054c8
0x89e8fc0, 0x89d7180
0x8874f80 (battle map loading?)
0x8874740, 0x88d6700, 0x88dbc40, 0x88e3140, 0x88f1040, 0x88f1980, 0x88f7e40, 0x88fa380, 0x890d440, 0x891f280,
0x893d340, 0x893e740, 0x893f640, 0x8946540

Use constants, especially for GE instructions!
  • Modding version: Other/Unknown

GeneralStrife


Dome

Quote from: GeneralStrife on February 13, 2012, 05:28:29 am
A noble goal

Indeed
Even if I highly dislike WOTL' script, the only reason I don't play WOTL is the slowdown

"Be wise today so you don't cry tomorrow"

Larkas

VERY interesting! Wish I could help with that, since, unlike many people, I really like WotL. Hey, who knows, maybe along the way you can find some way to even transplant stuff from WotL to the PSX version (Dark Knight's abilities come to mind)! Ehm, of course, that's not your goal, and I don't even know if it's possible =D

Well, anyways, keep up the great work! WotL would certainly be more enjoyable without all the slowdowns.

endrift

In case people don't realize, I've been rather perpetually updating the last few posts and putting stuff in the git repository usually multiple times a day. I have over 1000 lines of reverse engineered code at this point, and although it's rather aimless (like I just reverse engineered part of the event system because I didn't know where the program counter was going), I'm making progress all the time. I can post every time I add a function to the repo with information, but that might end up with me flooding the thread a bit (which is why I haven't been posting ever).

If anyone has the expertise, I could use a lot of help. Current estimates put me at needing 10 years if I have to reverse engineer the whole game! Hopefully I won't, because I don't think 10 years worth of my time trying to fix slowdown in one game for the PSP is a very good use of time.
  • Modding version: Other/Unknown

endrift



Now in native 340x240 glory.

No, I don't have a patch, I'm editing memory on the fly. If you have PSPLink, there's a script for resetting the screen size in the git repo at tools/disable-stretch.
  • Modding version: Other/Unknown

theultrawolf

This thread should be super-stickied! If this all goes to plan, then hacking WOTL won't be as much of a chore, and will actually be worth creating hacks for :D
  • Modding version: PSX & WotL


Eternal

  • Modding version: PSX & WotL
"You, no less human than we? Ha! Now there's a beastly thought. You've been less than we from the moment your baseborn father fell upon your mother in whatever gutter saw you sired! You've been chattel since you came into the world drenched in common blood!"
  • Discord username: eternal248#1817

endrift

Turns out I was on the wrong track entirely. I want to thank this thread for pointing me in the right direction: http://ffhacktics.com/smf/index.php?topic=6650.0

I'll get some real results for you guys soon enough. Until then, maybe I should just tease you guys with a video.
  • Modding version: Other/Unknown

Mega_Tyrant

I took a look at your notes, even though I have zero coding experience or knowledge.  Impressive discovery on your part and good luck with your endeavor, If you succeed you will have fixed what SE managed to break for no good reason. So what clued you in the right direction in that post?  For the most part it's just us saying we don't know why it works but FUSA somehow managed to sorta fix the slowdown. :P

RAWR!

endrift

The FuSa thread told me that the slowdown was fixed by something that tinkered with graphics code, and possibly graphics code that tinkered with timing. So I started looking at more graphics code, especially the ones that were related to timing. Wouldn't you know it, one of them was only called from one place, and when I messed with that one function...magic happened. So now I'm trying to figure out EXACTLY how that function works and what it does so I can replace it with one that works PROPERLY.

Right now it looks like the slowdown was caused intentionally. The function usually returns a value with bits 9:11 set to 0b001 or 0b000. When the framerate is halved, it's set to 0b010, and when it's thirded, it's set to 0b100. If I make it always return a value with bits 10 and 11 unset...it doesn't slow down. Sometimes it seems to speed up!
  • Modding version: Other/Unknown

Atma

Hmm, nice find.  It makes me shake my head to think it could have been done on purpose.  Was it needed for some limitation of the psp hardware?
My name is Atma... I am pure energy... and as ancient as the cosmos.


endrift

I've heard that rumor. Unfortunately, I don't have any friends who also have a copy of this game, so I can't test it out.

...although, I DO have two PSPs, and the game is downloadable on PSN these days.
  • Modding version: Other/Unknown

Eternal

Even if it does somehow affect multiplayer, it needs to go. Almost nobody uses Multiplayer.
  • Modding version: PSX & WotL
"You, no less human than we? Ha! Now there's a beastly thought. You've been less than we from the moment your baseborn father fell upon your mother in whatever gutter saw you sired! You've been chattel since you came into the world drenched in common blood!"
  • Discord username: eternal248#1817

Dome

Can't we just put the multiplayer items in the main game and remove that feature?

"Be wise today so you don't cry tomorrow"

Eternal

I tried, Dome. You can't put multiplayer items into ENTDs or Move-Finds. You can, however, edit them in the Patcher, rename them, etc.
  • Modding version: PSX & WotL
"You, no less human than we? Ha! Now there's a beastly thought. You've been less than we from the moment your baseborn father fell upon your mother in whatever gutter saw you sired! You've been chattel since you came into the world drenched in common blood!"
  • Discord username: eternal248#1817

Dome


"Be wise today so you don't cry tomorrow"