]> localhost Git - WindEmu.git/commitdiff
arm710: Support half-word transfers
authorGeorge Wright <gw@gwright.org.uk>
Wed, 24 Jun 2026 08:10:31 +0000 (01:10 -0700)
committerGeorge Wright <gw@gwright.org.uk>
Wed, 24 Jun 2026 08:11:47 +0000 (01:11 -0700)
WindCore/arm710.cpp
WindCore/arm710.h

index 31e16ddd8697f4e70ee71495e4cb88cb84701582..1496186e5c088a00411c8e40112b965e9e01e63f 100644 (file)
@@ -163,7 +163,9 @@ uint32_t ARM710::executeInstruction(uint32_t i) {
                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);
@@ -533,6 +535,89 @@ uint32_t ARM710::execSingleDataTransfer(uint32_t IPUBWL, uint32_t Rn, uint32_t R
        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);
index 0b0f048f6dcc6a48e25699c8038171f8676a988b..f0fa139a3fb14dd56f37e66f46d84e0458eb40e3 100644 (file)
@@ -26,7 +26,7 @@ typedef optional<uint32_t> MaybeU32;
 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)
@@ -298,6 +298,7 @@ private:
        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);