Release REICAST - Dreamcast Emulator


calc84maniac

Member
Joined
Mar 19, 2009
Messages
332
And calc84maniac, do you also have a good DIV algo?
You mean for the DIV0U/DIV0S/DIV1 instructions? I could perhaps try to come up with something. (I'm pretty interested in this sort of thing and I'd love to help out wherever I can.) Any hints as to how the M/Q/T flags are stored?

Edit: From what I can tell from the little bit I've looked at the dynarec, the T flag is typically stored in a register by itself, with the top 31 bits zero. Looking at the DIV1 algorithm, it looks like the Q and M bits are somewhat wasteful, because the algorithm can be done fully while keeping track of only Q==M. I think I've gotten DIV1 down to 8 ARM instructions, keeping Q==M in bit 31 of a register. M could optionally be kept in the register as well, the alternative being to just write it to SR as soon as the DIV0 instruction is executed.
 
Last edited by a moderator:

ptitSeb

Serial Porter
Joined
Aug 15, 2012
Messages
9,195
Age
49
Location
France, near Lyon
And calc84maniac, do you also have a good DIV algo?
You mean for the DIV0U/DIV0S/DIV1 instructions? I could perhaps try to come up with something. (I'm pretty interested in this sort of thing and I'd love to help out wherever I can.) Any hints as to how the M/Q/T flags are stored?
In fact, Reicast is doing some hle work here. It recognize standard division function, so it's the full division function I need.
 

commander-beef

Very Active Member
Joined
May 1, 2012
Messages
966
Location
Polandowo
Soul Calibur is my test game, I test improvement / regression mainly with this one, with the objectiv to have it FullSpeed with no sound stutering ... and it should not crash :)
Why didn't you use Shenmue as a test game, if you know what I mean :D
Most of beat'em up games uses a lot of timing routines, so it ts very accurate to use them as a test games.
 

calc84maniac

Member
Joined
Mar 19, 2009
Messages
332
And calc84maniac, do you also have a good DIV algo?
You mean for the DIV0U/DIV0S/DIV1 instructions? I could perhaps try to come up with something. (I'm pretty interested in this sort of thing and I'd love to help out wherever I can.) Any hints as to how the M/Q/T flags are stored?
In fact, Reicast is doing some hle work here. It recognize standard division function, so it's the full division function I need.
Oh, well, wouldn't any old division function work, then? What bit size are the inputs and outputs, anyway? Also, do you care about the correct Q and M flag outputs?

I edited my previous post while you were replying, but I think I came up with an 8-instruction DIV1 method, which could certainly help with division methods not recognized by HLE.
 

ptitSeb

Serial Porter
Joined
Aug 15, 2012
Messages
9,195
Age
49
Location
France, near Lyon
And calc84maniac, do you also have a good DIV algo?
You mean for the DIV0U/DIV0S/DIV1 instructions? I could perhaps try to come up with something. (I'm pretty interested in this sort of thing and I'd love to help out wherever I can.) Any hints as to how the M/Q/T flags are stored?
In fact, Reicast is doing some hle work here. It recognize standard division function, so it's the full division function I need.
Oh, well, wouldn't any old division function work, then? What bit size are the inputs and outputs, anyway? Also, do you care about the correct Q and M flag outputs?


I edited my previous post while you were replying, but I think I came up with an 8-instruction DIV1 method, which could certainly help with division methods not recognized by HLE.
Div is 32 bits -> 32 bits, signed and not signed. I tried a function I found on the net but it doesn't work. I think one of the emiter I coded must be wrong. I will code the thing in a small assembly file amd debug it also with gdb to check algo is right (I'll put a copy here also, but my current implementation doesn't works).

case shop_div32s:
{
// from http://www.chiark.greenend.org.uk/~theom/riscos/docs/ultimate/a252div.txt last method from Graeme Williams
MOV(r3, reg.mapg(op->rs2), true);
EOR(reg.mapg(op->rd2), reg.mapg(op->rd2), reg.mapg(op->rd2), CC_EQ);
EOR(reg.mapg(op->rd), reg.mapg(op->rd), reg.mapg(op->rd), CC_EQ);
B((17+32*3)*4-8, CC_EQ); // divide by zero special case
MOV(r0, 28); // cnt = r0, mod = r1, num = r2/rs1, den = r3/rs2
MOV(r1, reg.mapg(op->rs1), S_LSR, 4);
CMP(r3, r1, S_LSR, 12); SUB(r0, r0, 16, CC_LE); MOV(r1, r1, S_LSR, 16, CC_LE);
CMP(r3, r1, S_LSR, 4); SUB(r0, r0, 8, CC_LE); MOV(r1, r1, S_LSR, 8, CC_LE);
CMP(r3, r1); SUB(r0, r0, 4, CC_LE); MOV(r1, r1, S_LSR, 4, CC_LE);
MOV(r2, r2, S_LSL, r1); RSB(r3, r3, 0);
 
ADD(r2, r2, r2, true);
 
ADD(r0, r0, r0, S_LSL, 1); ADD(pc, pc, r0, S_LSL, 2); MOV(r0, r0);
for (int ii=0; ii<32; ii++) {
ADC(r1, r3, r1, true, S_LSL, 1); SUB(r1, r1, r3, CC_LO); ADC(r2, r2, r2, true);
}
//Label1:
MOV(reg.mapg(op->rd), r2);
MOV(reg.mapg(op->rd2), r1);
}
break;

And for DIV1, yes,I can try to implement it, thanks :)
 
Last edited by a moderator:

calc84maniac

Member
Joined
Mar 19, 2009
Messages
332
It looks like the problem with that routine is with this:

On Entry: num - is a signed numerator A (but +ve)
den - is a signed denominator B (but non-zero and +ve)
Blame Graeme for being a lazy typist, but it looks like this routine only takes positive ("+ve") inputs.

I'll give you the DIV0/DIV1 methods after I've worked on them some more, since I'd really like to see if I can whittle DIV1 down to 7 instructions or less.

Edit: Holy crap, I got DIV1 down to 6 instructions. Though I haven't tested it, I feel like this should work. I triple-checked all the possible flag inputs/outputs in a spreadsheet (and yes, I took into account that you get carry instead of borrow when you subtract by adding the negative).

Code in the spoiler:

Code:
// rQM is a register that contains Q==M in bit 31.
// It's up to you whether you want to flush Q and M to SR, but this particular implementation doesn't care.
// If you do care, then DIV0U should set SR.M=0 and DIV0S should set SR.M=(Rm>>31).
// In addition, when you eventually flush Q you should set SR.Q=((rQM>>31)==SR.M).
// If you want to retrieve rQM from SR, just set rQM=((SR.Q==SR.M)<<31).

/* DIV0U Rm,Rn */
    // Set Q==M to 1
    mov rQM,#0x80000000
    // Set T to 0
    mov rT,#0x00000000

/* DIV0S Rm,Rn */
    // Xor bit 31 of Rm and Rn
    eor rQM,Rm,Rn
    // Put result in T
    mov rT,rQM,lsr #31
    // Put inverted result in Q==M
    mvn rQM,rQM

/* DIV1 Rm,Rn */
    // If Q==M, r0=~Rm and C=1; else, r0=Rm and C=0
    eors r0,Rm,rQM,asr #32
    // Initialize output bit as (Q==M)^(Rn>>31)
    eor rQM,rQM,Rn
    // Shift Rn left by 1 and add T
    add rD,rT,Rn,lsl #1
    // Add or subtract Rm based on r0 and C
    adcs rD,rD,r0
    // If C is reset, invert output bit
    mvncc rQM,rQM
    // Set T to output bit (which happens to be Q==M)
    mov rT,rQM,lsr #31
 
Last edited by a moderator:

notaz

Certified Guru
Joined
Aug 23, 2005
Messages
4,913
Location
Lithuania
Website
notaz.gp2x.de
Last edited by a moderator:

drkIIRaziel

Still Fresh
Joined
Jun 23, 2008
Messages
68
It looks like the problem with that routine is with this:

On Entry: num - is a signed numerator A (but +ve)
den - is a signed denominator B (but non-zero and +ve)
Blame Graeme for being a lazy typist, but it looks like this routine only takes positive ("+ve") inputs.

I'll give you the DIV0/DIV1 methods after I've worked on them some more, since I'd really like to see if I can whittle DIV1 down to 7 instructions or less.

Edit: Holy crap, I got DIV1 down to 6 instructions. Though I haven't tested it, I feel like this should work. I triple-checked all the possible flag inputs/outputs in a spreadsheet (and yes, I took into account that you get carry instead of borrow when you subtract by adding the negative).

Code in the spoiler:

// rQM is a register that contains Q==M in bit 31.
// It's up to you whether you want to flush Q and M to SR, but this particular implementation doesn't care.
// If you do care, then DIV0U should set SR.M=0 and DIV0S should set SR.M=(Rm>>31).
// In addition, when you eventually flush Q you should set SR.Q=((rQM>>31)==SR.M).
// If you want to retrieve rQM from SR, just set rQM=((SR.Q==SR.M)<<31).

/* DIV0U Rm,Rn */
    // Set Q==M to 1
    mov rQM,#0x80000000
    // Set T to 0
    mov rT,#0x00000000

/* DIV0S Rm,Rn */
    // Xor bit 31 of Rm and Rn
    eor rQM,Rm,Rn
    // Put result in T
    mov rT,rQM,lsr #31
    // Put inverted result in Q==M
    mvn rQM,rQM

/* DIV1 Rm,Rn */
    // If Q==M, r0=~Rm and C=1; else, r0=Rm and C=0
    eors r0,Rm,rQM,asr #32
    // Initialize output bit as (Q==M)^(Rn>>31)
    eor rQM,rQM,Rn
    // Shift Rn left by 1 and add T
    add rD,rT,Rn,lsl #1
    // Add or subtract Rm based on r0 and C
    adcs rD,rD,r0
    // If C is reset, invert output bit
    mvncc rQM,rQM
    // Set T to output bit (which happens to be Q==M)
    mov rT,rQM,lsr #31
 That looks really nice! I'm not even sure I have il level opcodes for div0/div1 right now. Time to add them I guess :)
 

calc84maniac

Member
Joined
Mar 19, 2009
Messages
332
Hmm, this code kind of bothers me:


                case shop_rocl:
                        {
                                ADD(reg.mapg(op->rd),reg.mapg(op->rs2),reg.mapg(op->rs1),1,true); //(C,rd)= rs1<<1 + (|) rs2
                                MOVW(reg.mapg(op->rd2),0); //clear rd2 (for ADC/MOVCS)
                                ADC(reg.mapg(op->rd2),reg.mapg(op->rd2),0); //rd2=C (or MOVCS rd2, 1)
                        }
                        break;
The carry output from the ADD instruction is from the addition only; the carry from the shift is lost, so the carry out is always 0. This can be fixed by changing the ADD to an OR. If this is really the case, I'm surprised a lot of stuff isn't broken. Unless I'm missing something here.

Edit: Stupid quick reply box ate the stuff after the codebox
 
Last edited by a moderator:

ptitSeb

Serial Porter
Joined
Aug 15, 2012
Messages
9,195
Age
49
Location
France, near Lyon
Something wrong in it?

FYI, the "C" code (inside hw/sh4/dyna/shil_canonical.h) is

Code:
u64 rv;
((u32*)&rv)[0]=(r1<<1)|r2;
((u32*)&rv)[1]=r1>>31;
return rv;
 

Ian J

Advanced Member
Joined
Aug 27, 2010
Messages
1,880
I cannot get this to work on my CC unit.

I have Crazy Taxi and the BIOS files named as instructed.

The game runs fine on my phone but whichever video driver I try on the Pandora I cannot get past the 50/60hz screen refresh option.

On some drivers after selecting the 60hz option the top of the screen is corrupted but on others the screen goes black and gets no further.

Any help would be much appreciated.
 

ptitSeb

Serial Porter
Joined
Aug 15, 2012
Messages
9,195
Age
49
Location
France, near Lyon
So i should be ? I may have some emiter to create...

ORR(reg.mapg(op->rd),reg.mapg(op->rs2),reg.mapg(op->rs1),true, S_LSL, 1); //(C,rd)= rs1<<1 + (|) rs2
MOVW(reg.mapg(op->rd2),0); //clear rd2 (for ADC/MOVCS)
ADC(reg.mapg(op->rd2),reg.mapg(op->rd2),0); //rd2=C (or MOVCS rd2, 1)


I cannot get this to work on my CC unit.

I have Crazy Taxi and the BIOS files named as instructed.

The game runs fine on my phone but whichever video driver I try on the Pandora I cannot get past the 50/60hz screen refresh option.

On some drivers after selecting the 60hz option the top of the screen is corrupted but on others the screen goes black and gets no further.

Any help would be much appreciated.
I have issue with Crazy Taxi for now too. Not sure what it is yet.
 

calc84maniac

Member
Joined
Mar 19, 2009
Messages
332
So i should be ? I may have some emiter to create...


ORR(reg.mapg(op->rd),reg.mapg(op->rs2),reg.mapg(op->rs1),true, S_LSL, 1); //(C,rd)= rs1<<1 + (|) rs2
MOVW(reg.mapg(op->rd2),0); //clear rd2 (for ADC/MOVCS)
ADC(reg.mapg(op->rd2),reg.mapg(op->rd2),0); //rd2=C (or MOVCS rd2, 1)
Alternatively, you could reuse the ADC method, since it's functionally equivalent to ADC with the same two arguments.
 

ptitSeb

Serial Porter
Joined
Aug 15, 2012
Messages
9,195
Age
49
Location
France, near Lyon
So i should be ? I may have some emiter to create...


ORR(reg.mapg(op->rd),reg.mapg(op->rs2),reg.mapg(op->rs1),true, S_LSL, 1); //(C,rd)= rs1<<1 + (|) rs2
MOVW(reg.mapg(op->rd2),0); //clear rd2 (for ADC/MOVCS)
ADC(reg.mapg(op->rd2),reg.mapg(op->rd2),0); //rd2=C (or MOVCS rd2, 1)
Alternatively, you could reuse the ADC method, since it's functionally equivalent to ADC with the same two arguments.
Bah, the ORR seems to works, so I'll keep it like that :)

I'm still unabled to have a proper multiply. I think one of my emiter is broken, but I still don't know wich one. I will have to compile the asm on a side, and compare the output DWord by DWord to find the mistake :'(
 

drkIIRaziel

Still Fresh
Joined
Jun 23, 2008
Messages
68
Hmm, this code kind of bothers me:


                case shop_rocl:
                        {
                                ADD(reg.mapg(op->rd),reg.mapg(op->rs2),reg.mapg(op->rs1),1,true); //(C,rd)= rs1<<1 + (|) rs2
                                MOVW(reg.mapg(op->rd2),0); //clear rd2 (for ADC/MOVCS)
                                ADC(reg.mapg(op->rd2),reg.mapg(op->rd2),0); //rd2=C (or MOVCS rd2, 1)
                        }
                        break;
The carry output from the ADD instruction is from the addition only; the carry from the shift is lost, so the carry out is always 0. This can be fixed by changing the ADD to an OR. If this is really the case, I'm surprised a lot of stuff isn't broken. Unless I'm missing something here.

Edit: Stupid quick reply box ate the stuff after the codebox
Hrfm, looks like a brain fart. Well, there are quite a few broken games ;p
 
Top