-
Notifications
You must be signed in to change notification settings - Fork 12
psx/hw/cop: Support cfc2 and ctc2 op codes to access GTE Control Registers #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Very rough code to test that the approach works. closes ayrtonm#7
so we can hardcode rt=$at in the cop opcode
|
Somewhat answering my own question for a better way to form opcodes from registers: I did look at the I didn't want to list out every possible register, and am not quite sure how to determine the full range that are likely to occur. The psp comment talks about disassembling many examples. The |
|
@ayrtonm This is ready for review. I just noticed now that even after my refactor to use the "m" opcodes with LLVM, I am still bitwise forming EDIT: It's late and I'm possibly overthinking this ... the PSX doesn't have a co-processor 1 (FPU), so how this code forms |
Source: Hitmen psx docs https://hitmen.c02.at/files/docs/psx/psx.pdf p. 51 > GTE load and store instructions have a delay of 2 instructions, > for any GTE commands or operations accessing that > register. and local testing which showed sporadic errors in some examples. Sometimes the delays seem to occur naturally, so this was hard to verify. 2 nops after GTE opcodes and constructed opcodes seems to prevent all errors reliably.
closes #7
This PR
ctc1andcfc1(recognised by LLVM), and LLVM missingctc2,cfc2opcodes using.longsCopRegistermacro format, with"m"or blank for Data, and"c"for Controlmfcandmtc,nopaftermfcto ensure the result value is available in subsequent reads which seems to be necessary for reliability. There may be more delays needed for some of the others, but I have only fixed the issues I noticed in my examples. Happy to take advice on how to handle this better if needed.Technical:
I've used
$at/$1as temporary storage since I couldn't see a way to usein(reg)orout(reg)directly in bitwise forming the opcode. Theaddiu {}, $at, 0copies the result back to the register Rust expects it for theasm!macro to set the variable. This frequently results inaddiu $at, $at, 0, which does nothing. However, sometimes the Rust in/out register is not$at, so it is needed. If anyone knows of a better way to handle this, please let me know!Before merging this PR I should probably add in the full 64 GTE register definitions... DONE!
Co-Processor Op-code summary:
mfc0mfc0mfc1mfc1mfc2mfc2mtc0mtc0mtc1mtc1mtc2mtc2cfc1.longcfc2.longctc1.longctc2.longcop2Testing
I've got a separate project locally where I have tested:
SQR,const SQR_OP: u32 = 0x0a00428;which multiplies a vector by itself.<1, 2, 3>is squared to<1, 4, 9>:Simple, but performed by a previously inaccessible GTE :D
FLAG(r63) can be read and shows sensible error bits set based on the op ( 2 of the three registers have overflowed).Not covered in this PR:
Turns out LLVM does not support the
cop2opcode either. This is used to send GTE commands once the required registers have been set up. I used this in my example to test:I imagine adding this as a separate feature. I haven't really thought much about the full API needed, but to use the GTE properly the SDK needs Vectors, Matrices, and Colors of various sorts (with potentially different sizes depending on the context). And all the GTE op codes need to be defined.
Breaking all this up into separate manageable chunks seems sensible.