+// Taken from PEOPS SOFTGPU
+static inline boolean CheckForEndlessLoop(u32 laddr, u32 *lUsedAddr) {
+ if (laddr == lUsedAddr[1]) return TRUE;
+ if (laddr == lUsedAddr[2]) return TRUE;
+
+ if (laddr < lUsedAddr[0]) lUsedAddr[1] = laddr;
+ else lUsedAddr[2] = laddr;
+
+ lUsedAddr[0] = laddr;
+
+ return FALSE;
+}
+
+static u32 gpuDmaChainSize(u32 addr) {
+ u32 size;
+ u32 DMACommandCounter = 0;
+ u32 lUsedAddr[3];
+
+ lUsedAddr[0] = lUsedAddr[1] = lUsedAddr[2] = 0xffffff;
+
+ // initial linked list ptr (word)
+ size = 1;
+
+ do {
+ addr &= 0x1ffffc;
+
+ if (DMACommandCounter++ > 2000000) break;
+ if (CheckForEndlessLoop(addr, lUsedAddr)) break;
+
+ // # 32-bit blocks to transfer
+ size += psxMu8( addr + 3 );
+
+ // next 32-bit pointer
+ addr = psxMu32( addr & ~0x3 ) & 0xffffff;
+ size += 1;
+ } while (!(addr & 0x800000)); // contrary to some documentation, the end-of-linked-list marker is not actually 0xFF'FFFF
+ // any pointer with bit 23 set will do.
+
+ return size;
+}
+