/*
This file is part of jpcsp.
Jpcsp 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, either version 3 of the License, or
(at your option) any later version.
Jpcsp 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 for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.Allegrex;
/**
* Multiply Divide Unit, handles accumulators.
*
* @author hli
*/
public class MduState extends GprState {
public long hilo;
public void setHi(int value) {
hilo = (hilo & 0xffffffffL) | (((long) value) << 32);
}
public int getHi() {
return (int) (hilo >>> 32);
}
public void setLo(int value) {
hilo = (hilo & ~0xffffffffL) | ((value) & 0xffffffffL);
}
public int getLo() {
return (int) (hilo & 0xffffffffL);
}
@Override
public void reset() {
hilo = 0;
}
@Override
public void resetAll() {
super.resetAll();
hilo = 0;
}
public MduState() {
hilo = 0;
}
public void copy(MduState that) {
super.copy(that);
hilo = that.hilo;
}
public MduState(MduState that) {
super(that);
hilo = that.hilo;
}
public static final long signedDivMod(int x, int y) {
return (((long) (x % y)) << 32) | (((x / y)) & 0xffffffffL);
}
public static final long unsignedDivMod(long x, long y) {
return ((x % y) << 32) | ((x / y) & 0xffffffffL);
}
public final void doMFHI(int rd) {
if (rd != 0) {
setRegister(rd, getHi());
}
}
public final void doMTHI(int rs) {
int hi = getRegister(rs);
hilo = (((long) hi) << 32) | (hilo & 0xffffffffL);
}
public final void doMFLO(int rd) {
if (rd != 0) {
setRegister(rd, getLo());
}
}
public final void doMTLO(int rs) {
int lo = getRegister(rs);
hilo = (hilo & 0xffffffff00000000L) | ((lo) & 0x00000000ffffffffL);
}
public final void doMULT(int rs, int rt) {
hilo = ((long) getRegister(rs)) * ((long) getRegister(rt));
}
public final void doMULTU(int rs, int rt) {
hilo = (getRegister(rs) & 0xffffffffL) * (getRegister(rt) & 0xffffffffL);
}
public final void doDIV(int rs, int rt) {
int rsValue = getRegister(rs);
int rtValue = getRegister(rt);
if (rtValue == 0) {
// According to MIPS spec., result is unpredictable when dividing by zero.
// However on a PSP, hi is set to $rs register value and lo is set to 0x0000FFFF/0xFFFFFFFF.
// This has been tested on a real PSP using vfputest.pbp.
long lo = rsValue > 0xFFFF ? 0xFFFFFFFFL : 0x0000FFFFL;
hilo = (((long) rsValue) << 32) | lo;
} else {
int lo = rsValue / rtValue;
int hi = rsValue % rtValue;
hilo = (((long) hi) << 32) | ((lo) & 0xffffffffL);
}
}
public final void doDIVU(int rs, int rt) {
int rsValue = getRegister(rs);
int rtValue = getRegister(rt);
if (rtValue == 0) {
// According to MIPS spec., result is unpredictable when dividing by zero.
// However on a PSP, hi is set to $rs register value and lo is set to 0x0000FFFF/0xFFFFFFFF.
// This has been tested on a real PSP using vfputest.pbp.
long lo = rsValue > 0xFFFF ? 0xFFFFFFFFL : 0x0000FFFFL;
hilo = (((long) rsValue) << 32) | lo;
} else {
long x = rsValue & 0xFFFFFFFFL;
long y = rtValue & 0xFFFFFFFFL;
hilo = ((x % y) << 32) | ((x / y) & 0xFFFFFFFFL);
}
}
public final void doMADD(int rs, int rt) {
hilo += ((long) getRegister(rs)) * ((long) getRegister(rt));
}
public final void doMADDU(int rs, int rt) {
hilo += (getRegister(rs) & 0xffffffffL) * (getRegister(rt) & 0xffffffffL);
}
public final void doMSUB(int rs, int rt) {
hilo -= ((long) getRegister(rs)) * ((long) getRegister(rt));
}
public final void doMSUBU(int rs, int rt) {
hilo -= (getRegister(rs) & 0xffffffffL) * (getRegister(rt) & 0xffffffffL);
}
}