diff --git a/TODO.md b/TODO.md index a41107739..59e896005 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,11 @@ ## Instructions ``` -stbu -lwzu +need cr0: +andix +orx +mullwx +divwux addx addix @@ -12,13 +15,6 @@ subfx subfex subficx -mulli -mullwx -divwux - -andix -orx - rlwinmx rlwimix rldiclx diff --git a/include/xenia/cpu/ppc/instr.h b/include/xenia/cpu/ppc/instr.h index 6557f9037..065a8d8ab 100644 --- a/include/xenia/cpu/ppc/instr.h +++ b/include/xenia/cpu/ppc/instr.h @@ -73,6 +73,7 @@ typedef struct { // kXEPPCInstrFormatI struct { + // TODO(benvanik): doc format update uint32_t LK : 1; uint32_t AA : 1; uint32_t LI : 24; @@ -80,6 +81,7 @@ typedef struct { } I; // kXEPPCInstrFormatB struct { + // TODO(benvanik): doc format update uint32_t LK : 1; uint32_t AA : 1; uint32_t BD : 14; @@ -89,7 +91,9 @@ typedef struct { } B; // kXEPPCInstrFormatSC + // kXEPPCInstrFormatD struct { + // TODO(benvanik): doc format update uint32_t SIMM : 16; uint32_t A : 5; uint32_t D : 5; @@ -97,6 +101,7 @@ typedef struct { } D; // kXEPPCInstrFormatDS struct { + // TODO(benvanik): doc format update uint32_t : 2; uint32_t ds : 14; uint32_t A : 5; @@ -105,6 +110,7 @@ typedef struct { } DS; // kXEPPCInstrFormatX struct { + // TODO(benvanik): doc format update uint32_t Rc : 1; uint32_t : 10; uint32_t B : 5; @@ -114,6 +120,7 @@ typedef struct { } X; // kXEPPCInstrFormatXL struct { + // TODO(benvanik): doc format update uint32_t LK : 1; uint32_t : 10; uint32_t BB : 5; @@ -121,7 +128,9 @@ typedef struct { uint32_t BO : 5; uint32_t OPCD : 6; } XL; + // kXEPPCInstrFormatXFX struct { + // TODO(benvanik): doc format update uint32_t : 1; uint32_t : 10; uint32_t spr : 10; @@ -132,6 +141,7 @@ typedef struct { // kXEPPCInstrFormatXS // kXEPPCInstrFormatXO struct { + // TODO(benvanik): doc format update uint32_t Rc : 1; uint32_t : 8; uint32_t OE : 1; @@ -142,6 +152,15 @@ typedef struct { } XO; // kXEPPCInstrFormatA // kXEPPCInstrFormatM + struct { + uint32_t Rc : 1; + uint32_t ME : 5; + uint32_t MB : 5; + uint32_t SH : 5; + uint32_t RA : 5; + uint32_t RS : 5; + uint32_t OPCD : 6; + } M; // kXEPPCInstrFormatMD // kXEPPCInstrFormatMDS // kXEPPCInstrFormatVA diff --git a/src/cpu/codegen/emit_alu.cc b/src/cpu/codegen/emit_alu.cc index 2a5fa77bc..c4973f3fc 100644 --- a/src/cpu/codegen/emit_alu.cc +++ b/src/cpu/codegen/emit_alu.cc @@ -118,8 +118,28 @@ XEEMITTER(divwx, 0x7C0003D6, XO )(FunctionGenerator& g, IRBuilder<>& b, I } XEEMITTER(divwux, 0x7C000396, XO )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // dividend[0:31] <- (RA)[32:63] + // divisor[0:31] <- (RB)[32:63] + // RT[32:63] <- dividend ÷ divisor + // RT[0:31] <- undefined + + if (i.XO.Rc) { + // With cr0 update. + XEINSTRNOTIMPLEMENTED(); + return 1; + } + if (i.XO.OE) { + // With XER update. + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + Value* dividend = b.CreateTrunc(g.gpr_value(i.XO.A), b.getInt32Ty()); + Value* divisor = b.CreateTrunc(g.gpr_value(i.XO.B), b.getInt32Ty()); + Value* v = b.CreateUDiv(dividend, divisor); + v = b.CreateZExt(v, b.getInt64Ty()); + + return 0; } XEEMITTER(mulhdx, 0x7C000092, XO )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { @@ -148,13 +168,38 @@ XEEMITTER(mulldx, 0x7C0001D2, XO )(FunctionGenerator& g, IRBuilder<>& b, I } XEEMITTER(mulli, 0x1C000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // prod[0:127] <- (RA) × EXTS(SI) + // RT <- prod[64:127] + + // TODO(benvanik): ensure this has the right behavior when the value + // overflows. It should be truncating the result, but I'm not sure what LLVM + // does. + + Value* v = b.CreateMul(g.gpr_value(i.D.A), b.getInt64(XEEXTS16(i.D.SIMM))); + g.update_gpr_value(i.D.D, b.CreateTrunc(v, b.getInt64Ty())); + + return 0; } XEEMITTER(mullwx, 0x7C0001D6, XO )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // RT <- (RA)[32:63] × (RB)[32:63] + + if (i.XO.Rc) { + // With cr0 update. + XEINSTRNOTIMPLEMENTED(); + return 1; + } + if (i.XO.OE) { + // With XER update. + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + Value* v = b.CreateMul(b.CreateSExt(g.gpr_value(i.XO.A), b.getInt64Ty()), + b.CreateSExt(g.gpr_value(i.XO.B), b.getInt64Ty())); + g.update_gpr_value(i.XO.D, v); + + return 0; } XEEMITTER(negx, 0x7C0000D0, XO )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { @@ -594,6 +639,42 @@ XEEMITTER(rlwimix, 0x50000000, M )(FunctionGenerator& g, IRBuilder<>& b, I } XEEMITTER(rlwinmx, 0x54000000, M )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { + // n <- SH + // r <- ROTL32((RS)[32:63], n) + // m <- MASK(MB+32, ME+32) + // RA <- r & m + + if (i.M.Rc) { + // With cr0 update. + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + // The compiler will generate a bunch of these for the special case of + // SH=0, MB=ME + // Which seems to just select a single bit and set cr0 for use with a branch. + // We can detect this and do less work. + if (!i.M.SH && i.M.MB == i.M.ME) { + Value* v = b.CreateAnd(g.gpr_value(i.M.RS), 1 << i.M.MB); + g.update_gpr_value(i.M.RS, v); + return 0; + } + + // if mstart ≤ mstop then + // mask[mstart:mstop] = ones + // mask[all other bits] = zeros + // else + // mask[mstart:63] = ones + // mask[0:mstop] = ones + // mask[all other bits] = zeros + + // // ROTL32(x, y) = rotl(i64.(x||x), y) + // Value* v = b.CreateZExt(b.CreateTrunc(g.gpr_value(i.M.RS)), b.getInt64Ty()); + // v = b.CreateOr(b.CreateLShr(v, 32), v); + // // (v << shift) | (v >> (64 - shift)); + // v = b.CreateOr(b.CreateShl(v, i.M.SH), b.CreateLShr(v, 32 - i.M.SH)); + // v = b.CreateAnd(v, XEMASK(i.M.MB + 32, i.M.ME + 32)); + XEINSTRNOTIMPLEMENTED(); return 1; } diff --git a/src/cpu/codegen/emit_memory.cc b/src/cpu/codegen/emit_memory.cc index 1fbf3e426..eef8d1b28 100644 --- a/src/cpu/codegen/emit_memory.cc +++ b/src/cpu/codegen/emit_memory.cc @@ -215,8 +215,16 @@ XEEMITTER(lwz, 0x80000000, D )(FunctionGenerator& g, IRBuilder<>& b, I } XEEMITTER(lwzu, 0x84000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // EA <- (RA) + EXTS(D) + // RT <- i32.0 || MEM(EA, 4) + // RA <- EA + + Value* ea = b.CreateAdd(g.gpr_value(i.D.A), b.getInt64(XEEXTS16(i.D.SIMM))); + Value* v = g.ReadMemory(ea, 4, false); + g.update_gpr_value(i.D.D, v); + g.update_gpr_value(i.D.A, ea); + + return 0; } XEEMITTER(lwzux, 0x7C00006E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { @@ -264,8 +272,16 @@ XEEMITTER(stb, 0x98000000, D )(FunctionGenerator& g, IRBuilder<>& b, I } XEEMITTER(stbu, 0x9C000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // EA <- (RA) + EXTS(D) + // MEM(EA, 1) <- (RS)[56:63] + // RA <- EA + + Value* ea = b.CreateAdd(g.gpr_value(i.D.A), b.getInt64(XEEXTS16(i.D.SIMM))); + Value* v = g.gpr_value(i.D.D); + g.WriteMemory(ea, 1, v); + g.update_gpr_value(i.D.A, ea); + + return 0; } XEEMITTER(stbux, 0x7C0001EE, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {