Release Pcsx-Rearmed


stustaff said:
Wow!

This is pretty cool, just dropped one of my favourite games in this Psx-rearmed and it works perfectly at 600mhz framerate of about 45-60 with no changes.

Sound perfect even the voice at the start and graphics are spot on!

Thanks for this.
Which game?
 
Last edited by a moderator:
This looks like a really promising project. Just had a quick blast with Ridge Racer... was great to see it finally running at a decent speed. Will be great to play once nubs are in :)
 
meandu229 said:
stustaff said:
Wow!

This is pretty cool, just dropped one of my favourite games in this Psx-rearmed and it works perfectly at 600mhz framerate of about 45-60 with no changes.

Sound perfect even the voice at the start and graphics are spot on!

Thanks for this.
Which game?

Until I'm told otherwise, I'm assuming something from the Barbie detective series.

1281817866-00.jpg
 
Last edited by a moderator:
Today's debugging session:

* Alien resurrection does 'MFHI r0' (or was it MFLO?), which triggered assert in mov_assemble(). Assuming it doesn't indicate a bug I just removed the assert. The game wanted to do a fancy nop or something.
* Some game did MTC0 12 in delay slot. I've made it to clear cycles to force cc_interrupt just after the branch, no idea if this is right, but matches the interpreter.
* CTR reads r31 in JALR delay slot. As hlide explained old value would be used, and since we do the slot instruction first I just removed the assert.

* recompiler messes up on this block from Suikoden II; Ari64, could you take a look (I'm too tired):
Code:
* 8008ded8: BEQ r5,r0,8008df10
loop preload:
ldr r8,fp+144
<->
match=0
umull r12, r9, r8, r6
adds r10,r10,#18
bpl 0
tst r8,r8
bne 1
str r1,fp+152
str r9,fp+256
str r12,fp+252
branch: internal
b 0 (107312c+fef8cecc)
  8008dedc: MULTU r5,r9
* 8008dee0: ADDU r4,r8,r7
<->
add r3,r4,r1
  8008dee4: ADDIU r7,r7,-1
str r9,fp+256
str r12,fp+252
<->
sub r1,r1,#-1
  8008dee8: MFHI r11
<->
mov r4,r9
  8008deec: SRL r3,r11,3
<->
lsr r6,r4,#3
  8008def0: SLL r2,r3,2
str r4,fp+168
<->
lsl r2,r6,#2
  8008def4: ADDU r2,r2,r3
str r6,fp+136
<->
add r2,r2,r6
  8008def8: SLL r2,r2,1
<->
lsl r2,r2,#1
  8008defc: SUBU r2,r5,r2
<->
sub r2,r8,r2
  8008df00: ADDIU r2,r2,152
<->
add r2,r2,#152
  8008df04: SB r2,r4+0
<->
cmp r3,$2097152
bvc 0
mov r4,r3
strb r2,r4+0
ldrb lr,r0,r3 lsr #12
cmp lr,$1
bne 0
  8008df08: BGEZ r7,8008ded8
<->
smatch=0
mov r2,r6
cycle count (adj)
adds r10,r10,#24
bpl 0
tst r1,r1
bmi 1
sub r10,r10,#-14
str r2,fp+144
str r8,fp+144
ldr r0,fp+84
ldr r4,fp+156
ldr r6,fp+160
branch: internal
b 0 (10731ac+fef8ce4c)
  8008df0c: ADDU r5,r3,r0
The second time SB is executed the written value is incorrect. Seems like MIPS r5 gets aliased on both ARM's r2 and r8, why?
 
So it's confirmed I'm getting a Pandora. It should be here on Tuesday. Can someone give me the skinny on how well Final Fantasy VIII works on any of the PS1 emulators available for the Pandora? That's probably the game I'm most interested in. Thanks. :)
 
Last edited by a moderator:
notaz said:
* Alien resurrection does 'MFHI r0' (or was it MFLO?), which triggered assert in mov_assemble(). Assuming it doesn't indicate a bug I just removed the assert. The game wanted to do a fancy nop or something.
That's fine. That assert realy didn't need to be there. It was just there to catch junk that might not be real code.

notaz said:
* Some game did MTC0 12 in delay slot. I've made it to clear cycles to force cc_interrupt just after the branch, no idea if this is right, but matches the interpreter.
The catch here is that MTC0 12 can trigger an interrupt. The assert was there because I didn't implement the code to figure out what the correct return address was if the branch was taken. Resetting the cycle count can work, depending on how you designed your interrupt handler. It doesn't work that way in mupen64plus since MTC0() sets up the interrupt state and I didn't want that to happen twice.

notaz said:
* CTR reads r31 in JALR delay slot. As hlide explained old value would be used, and since we do the slot instruction first I just removed the assert.
Should be fine. The assert was there because I wasn't sure, so I wanted to catch this case if it ever showed up.

notaz said:
* recompiler messes up on this block from Suikoden II; Ari64, could you take a look (I'm too tired):
Hmm.. loop_preload acts on r8, which implies that pass 5 attempted to hoist a loop invariant. Possibly there's a bug there. I'd need to see the whole output including the register allocations.

If you just want to get it working, try removing the loop register allocation in pass 5.
 
Last edited by a moderator:
Gruso said:
I should have said PSX4pandora but I'm sure you knew what I meant! Version is psx4pandora10b5.pnd, in case it matters.

Where is beta 5 available? I thought beta 4 was the latest...
 
Last edited by a moderator:
I can see for any attempt to write in memory (SW, SB, etc.), you have :

Code:
cmp Rs,$2097152 // 0x200000  - 2 Mbytes boundary
  bvc 0

What's that for ?
 
Ari64 said:
Hmm.. loop_preload acts on r8, which implies that pass 5 attempted to hoist a loop invariant. Possibly there's a bug there. I'd need to see the whole output including the register allocations.
http://notaz.gp2x.de/tmp/log.gz

Ari64 said:
If you just want to get it working, try removing the loop register allocation in pass 5.
Forcing unneeded_reg=1 in unneeded_registers() also helps, but hurts output quality I guess.

hlide said:
I can see for any attempt to write in memory (SW, SB, etc.), you have :

Code:
cmp Rs,$2097152 // 0x200000  - 2 Mbytes boundary
  bvc 0

What's that for ?
to handle RAM writes fast (bvc goes to the memhandler). Daedalus, Mupen64 also use this trick.
 
Last edited by a moderator:
To clarify the MTC0 12 situation in case it is not clear...

Basically this is the MIPS instruction which enables/disables interrupts. If an interrupt comes in while interrupts are disabled, then it waits until interrupts are enabled again, and then the interrupt is taken.

Mupen64plus handles this with a function called check_interupt() which immediately triggers an interrupt if there was an unhandled interrupt while interrupts were disabled.

Normally the dynarec checks for interrupts at branches. When the cycle count reaches some limit then an interrupt is taken.

The bug that I ran into was with games that disable interrupts often. What would happen was that interrupts would be re-enabled by the MTC0 instruction, and then at the next branch the interrupt would be taken. There was the possibility that during those few instructions, there could be another interrupt. This would cause the first interrupt to be lost, and the game would hang waiting for an interrupt that would never happen.

So the fix is, the MTC0 12 handles the interrupt immediately, and no interrupts get lost.

If the MTC0 is in a delay slot, then there is no possibility of this happening since the interrupts get checked at the branch. In that case, you just have to make sure that the program counter is updated based on whether or not the branch was taken, so that it goes to the right place when it returns from the exception. I never found any N64 games that put the MTC0 in a branch delay slot, so I never tested that case.
 
notaz said:
http://notaz.gp2x.de/tmp/log.gz
The register allocation is clearly screwed up here. Let me look into it some more.

notaz said:
Ari64 said:
If you just want to get it working, try removing the loop register allocation in pass 5.
Forcing unneeded_reg=1 in unneeded_registers() also helps, but hurts output quality I guess.

Be careful with this - if you disable the liveness analysis, you also need to turn off constant propagation as it relies on this data for certain edge cases.

Doing this does generate less efficient code, around 5-10% slower. I doubt it really fixes the problem, just makes it less likely due to more register pressure and more frequent writebacks.
 
Last edited by a moderator:
Doing this in pass 5 helps:
Code:
@@ -9693,6 +9700,13 @@ int new_recompile_block(int addr)
             else f_regmap[hr]=-1;
           }
           else if(branch_regs[i].regmap[hr]>=0) f_regmap[hr]=branch_regs[i].regmap[hr];
+          // make sure mapping hasn't changed
+          int hr2;
+          for(hr2=0;hr2<HOST_REGS;hr2++)
+            if(hr2!=hr&&f_regmap[hr]==branch_regs[i].regmap[hr2]) {
+              f_regmap[hr]=-1;
+              break;
+            }
           if(itype[i+1]==STORE||itype[i+1]==STORELR||itype[i+1]==C1LS
           ||itype[i+1]==SHIFT||itype[i+1]==COP1||itype[i+1]==FLOAT
           ||itype[i+1]==FCOMP||itype[i+1]==FCONV
Not sure if it covers all cases though.
 
notaz said:
hlide said:
I can see for any attempt to write in memory (SW, SB, etc.), you have :

Code:
cmp Rs,$2097152 // 0x200000  - 2 Mbytes boundary
   bvc 0

What's that for ?
to handle RAM writes fast (bvc goes to the memhandler). Daedalus, Mupen64 also use this trick.

I was under (false ?) impression that PS1 games may run at kernel (0x80000000-like) addresses, not at user (0x00000000-like) addresses, so I wonder if such optimization is that relevant.
 
Last edited by a moderator:
Not only that, I don't get that stuff :
Code:
cmp Rs,$2097152
bvc 0
mov Base,Rs
strb Rt,Base+0

if I'm not wrong, Rs should contain a PS1 address in the range between 0x000000 and 0x1FFFFF, does it mean your executable maps Pandora virtual addresses between 0x000000 and 0x1FFFFF to a physical area of 2Mbytes (PS1 RAM) ? it really bugs me...
 
notaz said:
Doing this in pass 5 helps:
Code:
@@ -9693,6 +9700,13 @@ int new_recompile_block(int addr)
             else f_regmap[hr]=-1;
           }
           else if(branch_regs[i].regmap[hr]>=0) f_regmap[hr]=branch_regs[i].regmap[hr];
+          // make sure mapping hasn't changed
+          int hr2;
+          for(hr2=0;hr2<HOST_REGS;hr2++)
+            if(hr2!=hr&&f_regmap[hr]==branch_regs[i].regmap[hr2]) {
+              f_regmap[hr]=-1;
+              break;
+            }
           if(itype[i+1]==STORE||itype[i+1]==STORELR||itype[i+1]==C1LS
           ||itype[i+1]==SHIFT||itype[i+1]==COP1||itype[i+1]==FLOAT
           ||itype[i+1]==FCOMP||itype[i+1]==FCONV
Not sure if it covers all cases though.
I think that's it. The other case is below (comment says "no-match due to different register") and that one is handled correctly.

The code is kinda messy, but basically it starts at the branch instruction, looks at the history, then searches forward from the branch target. If the registers match, then it fills in the register allocation and hoists the load outside of the loop. The bug seems to be that it didn't clear the history if the register changed.

Hmm... loop_reg() tries to avoid this situation by keeping the register allocations consistent within a loop, where possible. That's probably why I never encountered this bug.
 
Last edited by a moderator:
hlide said:
if I'm not wrong, Rs should contain a PS1 address in the range between 0x000000 and 0x1FFFFF, does it mean your executable maps Pandora virtual addresses between 0x000000 and 0x1FFFFF to a physical area of 2Mbytes (PS1 RAM) ? it really bugs me...
If the address is between 0x000000 and 0x1FFFFF then the carry flag will be clear. If it's between 0x80000000 and 801FFFFF then the overflow flag will be set.

We don't care about the carry flag. :)
 
Last edited by a moderator:
Ari64 said:
I think that's it. The other case is below (comment says "no-match due to different register") and that one is handled correctly.
Good, was a bit worried about that big else block for non-branches.

Ari64 said:
hlide said:
if I'm not wrong, Rs should contain a PS1 address in the range between 0x000000 and 0x1FFFFF, does it mean your executable maps Pandora virtual addresses between 0x000000 and 0x1FFFFF to a physical area of 2Mbytes (PS1 RAM) ? it really bugs me...
If the address is between 0x000000 and 0x1FFFFF then the carry flag will be clear. If it's between 0x80000000 and 801FFFFF then the overflow flag will be set.

We don't care about the carry flag. :)
Right, and RAM is mmapped at 0x80000000 on host if it's still not clear enough.
 
Last edited by a moderator:
Back
Top