/*
* $Id$
*
* Copyright (C) 2003-2016 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.jnasm.assembler.x86;
import java.util.List;
import java.util.Map;
import org.jnode.assembler.Label;
import org.jnode.assembler.x86.X86Constants;
import org.jnode.assembler.x86.X86Register;
import org.jnode.jnasm.assembler.Address;
import org.jnode.jnasm.assembler.Instruction;
import org.jnode.jnasm.assembler.InstructionUtils;
import org.jnode.jnasm.assembler.Register;
/**
* @author Levente S\u00e1ntha (lsantha@users.sourceforge.net)
*/
public class FPU extends AbstractX86Module {
public static final int FADDP_ISN = 0;
public static final int FCHS_ISN = FADDP_ISN + 1;
public static final int FDIVP_ISN = FCHS_ISN + 1;
public static final int FILD_ISN = FDIVP_ISN + 1;
public static final int FISTP_ISN = FILD_ISN + 1;
public static final int FLD_ISN = FISTP_ISN + 1;
public static final int FMULP_ISN = FLD_ISN + 1;
public static final int FNSTSW_ISN = FMULP_ISN + 1;
public static final int FPREM_ISN = FNSTSW_ISN + 1;
public static final int FSTP_ISN = FPREM_ISN + 1;
public static final int FSUBP_ISN = FSTP_ISN + 1;
public static final int FUCOMPP_ISN = FSUBP_ISN + 1;
public static final int FXCH_ISN = FUCOMPP_ISN + 1;
protected static final Map<String, Integer> INSTRUCTION_MAP;
private static final String[] MNEMONICS;
static {
Map<String, Integer> map = InstructionUtils.getInstructionMap(FPU.class);
String[] mnemonics = InstructionUtils.getMnemonicArray(map);
INSTRUCTION_MAP = map;
MNEMONICS = mnemonics;
}
public FPU(Map<String, Label> labels, Map<String, Integer> constants) {
super(labels, constants);
}
String[] getMnemonics() {
return MNEMONICS;
}
public boolean emit(String mnemonic, List<Object> operands, int operandSize, Instruction instruction) {
this.operands = operands;
this.operandSize = operandSize;
Integer key = INSTRUCTION_MAP.get(mnemonic);
if (key == null) return false;
switch (key) {
case FADDP_ISN:
emitFADDP();
break;
case FCHS_ISN:
emitFCHS();
break;
case FDIVP_ISN:
emitFDIVP();
break;
case FILD_ISN:
emitFILD();
break;
case FISTP_ISN:
emitFISTP();
break;
case FLD_ISN:
emitFLD();
break;
case FMULP_ISN:
emitFMULP();
break;
case FNSTSW_ISN:
emitFNSTSW();
break;
case FPREM_ISN:
emitFPREM();
break;
case FSTP_ISN:
emitFSTP();
break;
case FSUBP_ISN:
emitFSUBP();
break;
case FUCOMPP_ISN:
emitFUCOMPP();
break;
case FXCH_ISN:
emitFXCH();
break;
default:
throw new Error("Invalid instruction binding " + key + " for " + mnemonic);
}
return true;
}
private void emitFADDP() {
int addr = getAddressingMode(1);
switch (addr) {
case R_ADDR:
X86Register.FPU r = getRegFPU(0);
stream.writeFADDP(r);
break;
default:
reportAddressingError(FADDP_ISN, addr);
}
}
private void emitFCHS() {
stream.writeFCHS();
}
private void emitFDIVP() {
int addr = getAddressingMode(1);
switch (addr) {
case R_ADDR:
X86Register.FPU r = getRegFPU(0);
stream.writeFDIVP(r);
break;
default:
reportAddressingError(FDIVP_ISN, addr);
}
}
private void emitFILD() {
int addr = getAddressingMode(1);
switch (addr) {
case E_ADDR:
Address ind = getAddress(0);
if (operandSize == X86Constants.BITS32) {
stream.writeFILD32(getRegister(ind.getImg()), ind.disp);
} else if (operandSize == X86Constants.BITS64) {
stream.writeFILD64(getRegister(ind.getImg()), ind.disp);
} else {
throw new IllegalArgumentException(
"Illegal operand size " + operandSize + " for " + getMnemonics()[FILD_ISN]);
}
break;
default:
reportAddressingError(FILD_ISN, addr);
}
}
private void emitFISTP() {
int addr = getAddressingMode(1);
switch (addr) {
case E_ADDR:
Address ind = getAddress(0);
if (operandSize == X86Constants.BITS32) {
stream.writeFISTP32(getRegister(ind.getImg()), ind.disp);
} else if (operandSize == X86Constants.BITS64) {
stream.writeFISTP64(getRegister(ind.getImg()), ind.disp);
} else {
throw new IllegalArgumentException(
"Illegal operand size " + operandSize + " for " + getMnemonics()[FISTP_ISN]);
}
break;
default:
reportAddressingError(FISTP_ISN, addr);
}
}
private void emitFLD() {
int addr = getAddressingMode(1);
switch (addr) {
case E_ADDR:
Address ind = getAddress(0);
if (operandSize == X86Constants.BITS32) {
stream.writeFLD32(getRegister(ind.getImg()), ind.disp);
} else if (operandSize == X86Constants.BITS64) {
stream.writeFLD64(getRegister(ind.getImg()), ind.disp);
} else {
throw new IllegalArgumentException(
"Illegal operand size " + operandSize + " for " + getMnemonics()[FLD_ISN]);
}
break;
case S_ADDR:
ind = getAddress(0);
if (operandSize == X86Constants.BITS32) {
stream.writeFLD32(getRegister(ind.getImg()), getRegister(ind.sreg), ind.scale, ind.disp);
} else if (operandSize == X86Constants.BITS64) {
stream.writeFLD64(getRegister(ind.getImg()), getRegister(ind.sreg), ind.scale, ind.disp);
} else {
throw new IllegalArgumentException(
"Illegal operand size " + operandSize + " for " + getMnemonics()[FLD_ISN]);
}
break;
default:
reportAddressingError(FLD_ISN, addr);
}
}
private void emitFMULP() {
int addr = getAddressingMode(1);
switch (addr) {
case R_ADDR:
X86Register.FPU r = getRegFPU(0);
stream.writeFMULP(r);
break;
default:
reportAddressingError(FMULP_ISN, addr);
}
}
private void emitFNSTSW() {
int addr = getAddressingMode(1);
switch (addr) {
case R_ADDR:
X86Register.GPR r = getReg(0);
if (r.equals(X86Register.GPR16.AX)) {
stream.writeFNSTSW_AX();
} else {
throw new IllegalArgumentException(
"Illegal operand " + r + " for " + getMnemonics()[FNSTSW_ISN]);
}
break;
default:
reportAddressingError(FNSTSW_ISN, addr);
}
}
private void emitFPREM() {
stream.writeFPREM();
}
private void emitFSTP() {
int addr = getAddressingMode(1);
switch (addr) {
case R_ADDR:
X86Register.FPU r = getRegFPU(0);
stream.writeFSTP(r);
break;
case E_ADDR:
Address ind = getAddress(0);
if (operandSize == X86Constants.BITS32) {
stream.writeFSTP32(getRegister(ind.getImg()), ind.disp);
} else if (operandSize == X86Constants.BITS64) {
stream.writeFSTP64(getRegister(ind.getImg()), ind.disp);
} else {
throw new IllegalArgumentException(
"Illegal operand size " + operandSize + " for " + getMnemonics()[FLD_ISN]);
}
break;
default:
reportAddressingError(FSTP_ISN, addr);
}
}
private void emitFSUBP() {
int addr = getAddressingMode(1);
switch (addr) {
case R_ADDR:
X86Register.FPU r = getRegFPU(0);
stream.writeFSUBP(r);
break;
default:
reportAddressingError(FSUBP_ISN, addr);
}
}
private void emitFUCOMPP() {
stream.writeFUCOMPP();
}
private void emitFXCH() {
int addr = getAddressingMode(1);
switch (addr) {
case R_ADDR:
X86Register.FPU r = getRegFPU(0);
stream.writeFXCH(r);
break;
default:
reportAddressingError(FXCH_ISN, addr);
}
}
final X86Register.FPU getRegFPU(int i) {
return getRegisterFPU(((Register) args[i]).name);
}
}