cycles += execMultiply(extract(i,21,20), extract(i,19,16), extract(i,15,12), extract(i,11,8), extract(i,3,0));
else if ((i & 0x0F8000F0) == 0x00800090 && isTVersion)
cycles += execMultiplyLong(extract(i,22,20), extract(i,19,16), extract(i,15,12), extract(i,11,8), extract(i,3,0));
- else if ((i & 0x0C000000) == 0x00000000)
+ else if ((i & 0x0E000090) == 0x00000090) {
+ cycles += execSingleHalfWordDataTransfer(extract(i, 24, 20), extract(i, 19, 16), extract(i, 15, 12), extract(i, 11, 8), extract(i, 6, 5), extract(i, 3, 0));
+ } else if ((i & 0x0C000000) == 0x00000000)
cycles += execDataProcessing(extract1(i,25), extract(i,24,21), extract1(i,20), extract(i,19,16), extract(i,15,12), extract(i,11,0));
else
raiseException(Undefined32, GPRs[15] - 8, 0x04);
return 2;
}
+uint32_t ARM710::execSingleHalfWordDataTransfer(uint32_t PUIWL, uint32_t Rn, uint32_t Rd, uint32_t addrMode1, uint32_t SH, uint32_t addrMode2)
+{
+ bool load = extract1(PUIWL, 0);
+ bool writeback = extract1(PUIWL, 1);
+ bool immediate = extract1(PUIWL, 2);
+ bool up = extract1(PUIWL, 3);
+ bool preIndex = extract1(PUIWL, 4);
+
+ uint32_t LSH = ((PUIWL << 2) | SH) & 0x7;
+ ValueSize valueSize;
+ bool isSigned;
+
+ switch (LSH) {
+ case 0x1:
+ valueSize = V16;
+ isSigned = false;
+ break;
+ case 0x5:
+ valueSize = V16;
+ isSigned = false;
+ break;
+ case 0x6:
+ valueSize = V8;
+ isSigned = true;
+ break;
+ case 0x7:
+ valueSize = V16;
+ isSigned = true;
+ break;
+ default:
+ // Invalid instruction on ARMv4 as it doesn't support double word ldr/str
+ return 2;
+ }
+
+ // calculate the offset
+ uint32_t calcOffset;
+ if (!immediate) {
+ // addrMode1 should be zero (SBZ)
+ // addrMode2 is Rm
+ calcOffset = GPRs[addrMode2];
+ } else {
+ // IMMEDIATE
+ // No rotation or anything here
+ // addrMode1 is the high 4 bits
+ // addrMode2 is the low 4 bits
+ calcOffset = (addrMode1 << 4) | addrMode2;
+ }
+
+ uint32_t base = GPRs[Rn];
+ if (Rn == 15) base -= 4; // prefetch adjustment
+ uint32_t modifiedBase = up ? (base + calcOffset) : (base - calcOffset);
+ uint32_t transferAddr = preIndex ? modifiedBase : base;
+
+ bool changeModes = !preIndex && writeback && isPrivileged();
+ auto saveMode = currentMode();
+
+ MMUFault fault;
+
+ if (load) {
+ if (changeModes) switchMode(User32);
+ auto readResult = readVirtual(transferAddr, valueSize);
+ if (changeModes) switchMode(saveMode);
+ if (readResult.first.has_value()) {
+ GPRs[Rd] = readResult.first.value();
+ if (Rd == 15) prefetchCount = 0;
+ }
+ fault = readResult.second;
+ } else {
+ uint32_t value = GPRs[Rd];
+ if (changeModes) switchMode(User32);
+ fault = writeVirtual(value, transferAddr, valueSize);
+ if (changeModes) switchMode(saveMode);
+ }
+
+ if ((preIndex && writeback) || !preIndex)
+ GPRs[Rn] = modifiedBase;
+
+ if (fault != NoFault)
+ reportFault(fault);
+
+ return 2;
+}
+
uint32_t ARM710::execBlockDataTransfer(uint32_t PUSWL, uint32_t Rn, uint32_t registerList)
{
bool load = extract1(PUSWL, 0);
class ARM710
{
public:
- enum ValueSize { V8 = 0, V32 = 1 };
+ enum ValueSize { V8 = 0, V16 = 1, V32 = 2 };
enum MMUFault : uint64_t {
// ref: datasheet 9-13 (p111)
uint32_t execMultiplyLong(uint32_t UAS, uint32_t RdHi, uint32_t RdLo, uint32_t Rs, uint32_t Rm);
uint32_t execSingleDataSwap(bool B, uint32_t Rn, uint32_t Rd, uint32_t Rm);
uint32_t execSingleDataTransfer(uint32_t IPUBWL, uint32_t Rn, uint32_t Rd, uint32_t offset);
+ uint32_t execSingleHalfWordDataTransfer(uint32_t PUIWL, uint32_t Rn, uint32_t Rd, uint32_t addrMode1, uint32_t SH, uint32_t addrMode2);
uint32_t execBlockDataTransfer(uint32_t PUSWL, uint32_t Rn, uint32_t registerList);
uint32_t execBranch(bool L, uint32_t offset);
uint32_t execCP15RegisterTransfer(uint32_t CPOpc, bool L, uint32_t CRn, uint32_t Rd, uint32_t CP, uint32_t CRm);