diff --git a/.gitignore b/.gitignore index 1899660..1dc393d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build -.vscode \ No newline at end of file +.vscode +test \ No newline at end of file diff --git a/src/cpu/CPU.cpp b/src/cpu/CPU.cpp index 30a16ff..165efb0 100644 --- a/src/cpu/CPU.cpp +++ b/src/cpu/CPU.cpp @@ -24,14 +24,16 @@ CPUThread::CPUThread(uint32_t entryPoint, uint32_t stackSize, XexLoader& ref) uint32_t pcrAddress = Memory::VirtAllocMemoryRange(0xE0000000, 0xFFD00000, 0x2D8); Memory::AllocMemory(pcrAddress, 0x2D8); + state.pcr_address = pcrAddress; - uint32_t tlsAddr = Memory::VirtAllocMemoryRange(0xE0000000, 0xFFD00000, 4096); - Memory::AllocMemory(tlsAddr, 4096); + state.tls_addr = Memory::VirtAllocMemoryRange(0xE0000000, 0xFFD00000, 4096); + Memory::AllocMemory(state.tls_addr, 4096); + state.tls_lowest_alloced = 0x80; uint32_t xthreadAddr = Memory::VirtAllocMemoryRange(0xE0000000, 0xFFD00000, 4096); Memory::AllocMemory(xthreadAddr, 4096); - Memory::Write32(pcrAddress+0x00, tlsAddr); + Memory::Write32(pcrAddress+0x00, state.tls_addr); Memory::Write32(pcrAddress+0x100, xthreadAddr); printf("Stack base is 0x%08x\n", stackBase); @@ -51,6 +53,38 @@ void CPUThread::Run() { twi(instr); } + else if (((instr >> 26) & 0x3F) == 4 && ((instr >> 4) & 0x7F) == 12 && (instr & 3) == 3) + { + lvx128(instr); + } + else if (((instr >> 26) & 0x3F) == 4 && ((instr >> 4) & 0x7F) == 28 && (instr & 3) == 3) + { + stvx128(instr); + } + else if (((instr >> 26) & 0x3F) == 4 && (instr & 0x7FF) == 260) + { + vslb(instr); + } + else if (((instr >> 26) & 0x3F) == 4 && (instr & 0x7FF) == 524) + { + vspltb(instr); + } + else if (((instr >> 26) & 0x3F) == 4 && (instr & 0x7FF) == 780) + { + vspltisb(instr); + } + else if (((instr >> 26) & 0x3F) == 4 && (instr & 0x7FF) == 844) + { + vspltish(instr); + } + else if (((instr >> 26) & 0x3F) == 4 && (instr & 0x7FF) == 1156) + { + vor(instr); + } + else if (((instr >> 26) & 0x3F) == 6 && ((instr >> 4) & 0x7F) == 119) + { + vspltisw128(instr); + } else if (((instr >> 26) & 0x3F) == 7) { mulli(instr); @@ -71,6 +105,10 @@ void CPUThread::Run() { addic(instr); } + else if (((instr >> 26) & 0x3F) == 13) + { + addicx(instr); + } else if (((instr >> 26) & 0x3F) == 14) { addi(instr); @@ -95,6 +133,10 @@ void CPUThread::Run() { bclr(instr); } + else if (((instr >> 26) & 0x3F) == 19 && ((instr >> 1) & 0x3FF) == 528) + { + bctr(instr); + } else if (((instr >> 26) & 0x3F) == 20) { rlwimi(instr); @@ -115,10 +157,26 @@ void CPUThread::Run() { andi(instr); } + else if (((instr >> 26) & 0x3F) == 29) + { + andis(instr); + } + else if (((instr >> 26) & 0x3F) == 30 && ((instr >> 2) & 0x7) == 0) + { + rldicl(instr); + } + else if (((instr >> 26) & 0x3F) == 30 && ((instr >> 2) & 0x7) == 1) + { + rldicr(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 0) { cmp(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 6) + { + lvsl(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 8) { subfc(instr); @@ -131,6 +189,18 @@ void CPUThread::Run() { lwzx(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 24) + { + slw(instr); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 26) + { + cntlzw(instr); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 27) + { + sld(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 28) { and_(instr); @@ -151,10 +221,26 @@ void CPUThread::Run() { mfmsr(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 87) + { + lbzx(instr); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 104) + { + neg(instr); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 124) + { + nor(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 136) { subfe(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 149) + { + stdx(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 150) { stwcx(instr); @@ -191,6 +277,10 @@ void CPUThread::Run() { dcbt(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 316) + { + xor_(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 339) { mfspr(instr); @@ -199,6 +289,10 @@ void CPUThread::Run() { mftb(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 407) + { + sthx(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 444) { or_(instr); @@ -211,18 +305,50 @@ void CPUThread::Run() { mtspr(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 489) + { + divd(instr); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 598) + { + printf("sync 1\n"); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 647) + { + stvlx(instr); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 662) + { + stwbrx(instr); + } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 679) + { + stvrx(instr); + } else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 824) { srawi(instr); } + else if (((instr >> 26) & 0x3F) == 31 && ((instr >> 1) & 0x3FF) == 1014) + { + dcbz(instr); + } else if (((instr >> 26) & 0x3F) == 32) { lwz(instr); } + else if (((instr >> 26) & 0x3F) == 33) + { + lwzu(instr); + } else if (((instr >> 26) & 0x3F) == 34) { lbz(instr); } + else if (((instr >> 26) & 0x3F) == 35) + { + lbzu(instr); + } else if (((instr >> 26) & 0x3F) == 36) { stw(instr); @@ -235,6 +361,10 @@ void CPUThread::Run() { stb(instr); } + else if (((instr >> 26) & 0x3F) == 39) + { + stbu(instr); + } else if (((instr >> 26) & 0x3F) == 40) { lhz(instr); @@ -247,18 +377,62 @@ void CPUThread::Run() { lfs(instr); } + else if (((instr >> 26) & 0x3F) == 50) + { + lfd(instr); + } else if (((instr >> 26) & 0x3F) == 52) { stfs(instr); } + else if (((instr >> 26) & 0x3F) == 54) + { + stfd(instr); + } else if (((instr >> 26) & 0x3F) == 58) { ld(instr); } + else if (((instr >> 26) & 0x3F) == 59 && ((instr >> 1) & 0x1F) == 22) + { + fsqrt(instr); + } + else if (((instr >> 26) & 0x3F) == 59 && ((instr >> 1) & 0x1F) == 25) + { + fmuls(instr); + } else if (((instr >> 26) & 0x3F) == 62) { std(instr); } + else if (((instr >> 26) & 0x3F) == 63 && ((instr >> 1) & 0x3FF) == 0) + { + fcmpu(instr); + } + else if (((instr >> 26) & 0x3F) == 63 && ((instr >> 1) & 0x3FF) == 12) + { + frsp(instr); + } + else if (((instr >> 26) & 0x3F) == 63 && ((instr >> 1) & 0x3FF) == 18) + { + fdiv(instr); + } + else if (((instr >> 26) & 0x3F) == 63 && ((instr >> 1) & 0x3FF) == 22) + { + fsqrt(instr); + } + else if (((instr >> 26) & 0x3F) == 63 && ((instr >> 1) & 0x3FF) == 25) + { + fmul(instr); + } + else if (((instr >> 26) & 0x3F) == 63 && ((instr >> 1) & 0x3FF) == 814) + { + fctid(instr); + } + else if (((instr >> 26) & 0x3F) == 63 && ((instr >> 1) & 0x3FF) == 846) + { + fcfid(instr); + } else { printf("Failed to execute instruction: 0x%08x\n", instr); @@ -271,7 +445,9 @@ void CPUThread::Dump() for (int i = 0; i < 32; i++) printf("r%d\t->\t0x%08lx\n", i, state.regs[i]); for (int i = 0; i < 32; i++) - printf("fr%d\t->\t%0.2f\n", i, state.fr[i].f); + printf("fr%d\t->\t%f\n", i, state.fr[i].d); + for (int i = 0; i < 128; i++) + printf("v%d\t->\t0x%016lx%016lx\n", i, state.vfr[i].u64[1], state.vfr[i].u64[0]); for (int i = 0; i < 7; i++) printf("cr%d\t->\t%d\n", i, state.GetCR(i)); printf("[%s]\n", state.xer.ca ? "c" : "."); diff --git a/src/cpu/CPU.h b/src/cpu/CPU.h index 628ac22..9666c91 100644 --- a/src/cpu/CPU.h +++ b/src/cpu/CPU.h @@ -12,6 +12,10 @@ class XexLoader; /// We declare this in a struct so the scheduler can access it to save/restore CPU state on a context switch typedef struct { + uint32_t pcr_address; + uint32_t tls_addr; + uint32_t tls_lowest_alloced; + uint64_t pc; uint64_t regs[32]; uint64_t ctr; @@ -20,10 +24,13 @@ typedef struct union { - uint32_t u; - float f; + uint64_t u; + double d; } fr[32]; + uint128_t vfr[128]; + uint128_t vscr_vec; + struct { uint32_t cr0; @@ -107,32 +114,53 @@ class CPUThread XexLoader& xexRef; private: void twi(uint32_t instruction); // 3 + void lvx128(uint32_t instruction); // 4 12 + void stvx128(uint32_t instruction); // 4 30 + void vslb(uint32_t instruction); // 4 260 + void vspltb(uint32_t instruction); // 4 524 + void vspltisb(uint32_t instruction); // 4 780 + void vspltish(uint32_t instruction); // 4 844 + void vspltisw128(uint32_t instruction); // 4 119 + void vor(uint32_t instruction); // 4 1156 void mulli(uint32_t instruction); // 7 void subfic(uint32_t instruction); // 8 void cmpli(uint32_t instruction); // 10 void cmpi(uint32_t instruction); // 11 void addic(uint32_t instruction); // 12 + void addicx(uint32_t instruction); // 13 void addi(uint32_t instruction); // 14 void addis(uint32_t instruction); // 15 void bc(uint32_t instruction); // 16 void sc(uint32_t instruction); // 17 void branch(uint32_t instruction); // 18 void bclr(uint32_t instruction); // 19 16 + void bctr(uint32_t instruction); // 19 528 void rlwimi(uint32_t instruction); // 20 void rlwinm(uint32_t instruction); // 21 void ori(uint32_t instruction); // 24 void oris(uint32_t instruction); // 25 void andi(uint32_t instruction); // 28 + void andis(uint32_t instruction); // 29 + void rldicl(uint32_t instruction); // 30 0 + void rldicr(uint32_t instruction); // 30 1 void cmp(uint32_t instruction); // 31 0 + void lvsl(uint32_t instruction); // 31 6 void subfc(uint32_t instruction); // 31 8 void lwarx(uint32_t instruction); // 31 20 void lwzx(uint32_t instruction); // 31 23 + void slw(uint32_t instruction); // 31 24 + void cntlzw(uint32_t instruction); // 31 26 + void sld(uint32_t instruction); // 31 27 void and_(uint32_t instruction); // 31 28 void cmpl(uint32_t instruction); // 31 32 void subf(uint32_t instruction); // 31 40 void andc(uint32_t instruction); // 31 60 void mfmsr(uint32_t instruction); // 31 83 + void lbzx(uint32_t instruction); // 31 87 + void neg(uint32_t instruction); // 31 104 + void nor(uint32_t instruction); // 31 124 void subfe(uint32_t instruction); // 31 136 + void stdx(uint32_t instruction); // 31 149 void stwcx(uint32_t instruction); // 31 150 void stwx(uint32_t instruction); // 31 151 void mtmsrd(uint32_t instruction); // 31 178 @@ -141,23 +169,43 @@ class CPUThread void mullw(uint32_t instruction); // 31 235 void add(uint32_t instruction); // 31 266 void dcbt(uint32_t instruction); // 31 278 + void xor_(uint32_t instruction); // 31 316 void mfspr(uint32_t instruction); // 31 339 void mftb(uint32_t instruction); // 31 371 + void sthx(uint32_t instruction); // 31 407 void or_(uint32_t instruction); // 31 444 void divwu(uint32_t instruction); // 31 459 void mtspr(uint32_t instruction); // 31 467 + void divd(uint32_t instruction); // 31 489 + void stvlx(uint32_t instruction); // 31 647 + void stwbrx(uint32_t instruction); // 31 662 + void stvrx(uint32_t instruction); // 31 679 void srawi(uint32_t instruction); // 31 824 + void dcbz(uint32_t instruction); // 31 1014 void lwz(uint32_t instruction); // 32 + void lwzu(uint32_t instruction); // 33 void lbz(uint32_t instruction); // 34 + void lbzu(uint32_t instruction); // 35 void stw(uint32_t instruction); // 36 void stwu(uint32_t instruction); // 37 void stb(uint32_t instruction); // 38 + void stbu(uint32_t instruction); // 39 void lhz(uint32_t instruction); // 40 void sth(uint32_t instruction); // 44 void lfs(uint32_t instruction); // 48 + void lfd(uint32_t instruction); // 50 void stfs(uint32_t instruction); // 52 + void stfd(uint32_t instruction); // 54 void ld(uint32_t instruction); // 58 + void fmuls(uint32_t instruction); // 59 25 void std(uint32_t instruction); // 62 + void fcmpu(uint32_t instruction); // 63 0 + void frsp(uint32_t instruction); // 63 12 + void fdiv(uint32_t instruction); // 63 18 + void fsqrt(uint32_t instruction); // 63 22 + void fmul(uint32_t instruction); // 63 25 + void fctid(uint32_t instruction); // 63 814 + void fcfid(uint32_t instruction); // 63 846 private: bool CondPassed(uint8_t bo, uint8_t bi); private: diff --git a/src/cpu/ops.cpp b/src/cpu/ops.cpp index 08744c2..301eed7 100644 --- a/src/cpu/ops.cpp +++ b/src/cpu/ops.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -66,6 +68,115 @@ void CPUThread::twi(uint32_t instruction) } } +void CPUThread::lvx128(uint32_t instruction) +{ + uint32_t vd = ((instruction >> 21) & 0x1F) | (((instruction >> 2) & 0x3) << 5); + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + state.vfr[vd].u128 = Memory::Read128(ea); + + printf("lvx128 v%d, r%d, r%d\n", vd, ra, rb); +} + +void CPUThread::stvx128(uint32_t instruction) +{ + uint32_t vd = ((instruction >> 21) & 0x1F) | (((instruction >> 2) & 0x3) << 5); + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + Memory::Write128(ea, state.vfr[vd].u128); + + printf("stvx128 v%d, r%d, r%d\n", vd, ra, rb); +} + +void CPUThread::vslb(uint32_t instruction) +{ + uint8_t vd = (instruction >> 21) & 0x1F; + uint8_t va = (instruction >> 16) & 0x1F; + uint8_t vb = (instruction >> 11) & 0x1F; + + for (int i = 0; i < 16; i++) + { + state.vfr[vd].u8[i] = state.vfr[va].u8[i] << (state.vfr[vb].u8[i] & 7); + } + + printf("vslb v%d,v%d,v%d\n", vd, va, vb); +} + +void CPUThread::vspltb(uint32_t instruction) +{ + uint8_t vd = (instruction >> 21) & 0x1F; + uint8_t uimm = (instruction >> 16) & 0x1F; + uint8_t vb = (instruction >> 11) & 0x1F; + + uint8_t val = state.vfr[vb].u8[uimm]; + + for (int i = 0; i < 16; i++) + state.vfr[vd].u8[i] = val; + + printf("vspltb v%d,v%d,%d\n", vd, vb, uimm); +} + +void CPUThread::vspltisb(uint32_t instruction) +{ + uint8_t vd = (instruction >> 21) & 0x1F; + uint8_t imm = (instruction >> 16) & 0x1F; + imm = (imm & 0x10) ? (uint8_t)(imm | 0xF0) : imm; + + for (int i = 0; i < 16; i++) + state.vfr[vd].u8[i] = imm; + + printf("vspltisb v%d,%d\n", vd, imm); +} + +void CPUThread::vspltish(uint32_t instruction) +{ + uint8_t vd = (instruction >> 21) & 0x1F; + uint16_t imm = (instruction >> 16) & 0x1F; + imm = (imm & 0x10) ? (uint16_t)(imm | 0xFFF0) : imm; + + for (int i = 0; i < 8; i++) + state.vfr[vd].u16[i] = imm; + + printf("vspltish v%d,%d\n", vd, imm); +} + +void CPUThread::vspltisw128(uint32_t instruction) +{ + uint32_t vd = ((instruction >> 21) & 0x1F) | (((instruction >> 2) & 0x3) << 5); + uint32_t imm = (instruction >> 11) & 0x1F; + imm = (imm & 0x10) ? (uint32_t)-1 : imm; + + for (int i = 0; i < 4; i++) + state.vfr[vd].u32[i] = imm; + + printf("vspltisw128 v%d, 0x%08x\n", vd, imm); +} + +void CPUThread::vor(uint32_t instruction) +{ + uint8_t vd = (instruction >> 21) & 0x1F; + uint8_t va = (instruction >> 16) & 0x1F; + uint8_t vb = (instruction >> 11) & 0x1F; + + state.vfr[vd].u128 = state.vfr[va].u128 | state.vfr[vb].u128; + + printf("vor v%d,v%d,v%d\n", vd, va, vb); +} + void CPUThread::mulli(uint32_t instruction) { uint8_t rt = (instruction >> 21) & 0x1F; @@ -83,7 +194,7 @@ void CPUThread::subfic(uint32_t instruction) uint8_t ra = (instruction >> 16) & 0x1F; uint64_t simm = (uint64_t)(int64_t)(int16_t)(instruction & 0xFFFF); - state.regs[rt] = simm - state.regs[ra]; + state.regs[rt] = ~state.regs[ra] + simm + 1; uint32_t trunc_v2 = (uint32_t)state.regs[ra]; state.xer.ca = ((uint32_t)simm > (trunc_v2-1)) | (!trunc_v2); @@ -131,7 +242,7 @@ void CPUThread::cmpi(uint32_t instruction) void CPUThread::addic(uint32_t instruction) { - int16_t si = (int16_t)(instruction & 0xFFFF); + uint64_t si = (int64_t)(int16_t)(instruction & 0xFFFF); uint8_t rt = (instruction >> 21) & 0x1F; uint8_t ra = (instruction >> 16) & 0x1F; @@ -139,9 +250,28 @@ void CPUThread::addic(uint32_t instruction) printf("subic r%d,r%d,%d\n", rt, ra, -si); else printf("addic r%d,r%d,%d\n", rt, ra, si); + + state.xer.ca = ((uint32_t)si < !((uint32_t)state.regs[ra])); + state.regs[rt] = state.regs[ra] + (int64_t)si; +} + +void CPUThread::addicx(uint32_t instruction) +{ + int16_t si = (int16_t)(instruction & 0xFFFF); + uint8_t rt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + + if (si < 0) + printf("subic. r%d,r%d,%d\n", rt, ra, -si); + else + printf("addic. r%d,r%d,%d\n", rt, ra, si); state.xer.ca = ((uint32_t)(int64_t)si < !((uint32_t)state.regs[ra])); + + state.regs[rt] = state.regs[ra] + (int64_t)si; + + state.UpdateCRn(state.regs[rt], 0, 0); } void CPUThread::addi(uint32_t instruction) @@ -248,12 +378,28 @@ void CPUThread::bclr(uint32_t instruction) if (CondPassed(bo, bi)) { - state.pc = state.lr; + state.pc = old_lr; } printf("bclr\n"); } +void CPUThread::bctr(uint32_t instruction) +{ + uint8_t bo = (instruction >> 21) & 0x1F; + uint8_t bi = (instruction >> 16) & 0x1F; + bool lk = instruction & 1; + + if (lk) state.lr = state.pc; + + if (CondPassed(bo, bi)) + { + state.pc = state.ctr; + } + + printf("bctr\n"); +} + void CPUThread::rlwimi(uint32_t instruction) { uint8_t rs = (instruction >> 21) & 0x1F; @@ -326,6 +472,44 @@ void CPUThread::andi(uint32_t instruction) printf("andi. r%d,r%d,0x%04x\n", ra, rs, ui); } +void CPUThread::andis(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint32_t ui = (instruction & 0xFFFF) << 16; + + state.regs[ra] = state.regs[rs] & ui; + state.UpdateCRn(state.regs[ra], 0, 0); + + printf("andis. r%d,r%d,0x%04x\n", ra, rs, ui); +} + +void CPUThread::rldicl(uint32_t instruction) +{ + uint8_t rt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint16_t sh = ((instruction >> 11) & 0x1F) | (((instruction >> 1) & 1) << 5); + uint16_t mb = ((instruction >> 6) & 0x1F) | (((instruction >> 5) & 0x1) << 5); + + uint64_t m = XEMASK(mb, 63); + state.regs[ra] = (std::rotl(state.regs[rt], sh) & m); + + printf("rldicl r%d,r%d,%d,%d\n", rt, ra, sh, mb); +} + +void CPUThread::rldicr(uint32_t instruction) +{ + uint8_t rt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint16_t sh = ((instruction >> 11) & 0x1F) | (((instruction >> 1) & 1) << 5); + uint16_t mb = ((instruction >> 6) & 0x1F) | (((instruction >> 5) & 0x1) << 5); + + uint64_t m = XEMASK(0, mb); + state.regs[ra] = (std::rotl(state.regs[rt], sh) & m); + + printf("rldicr r%d,r%d,%d,%d\n", rt, ra, sh, mb); +} + void CPUThread::cmp(uint32_t instruction) { uint8_t ra = (instruction >> 16) & 0x1F; @@ -349,6 +533,47 @@ void CPUThread::cmp(uint32_t instruction) } } +#define UINT128(hi, lo) (((__uint128_t) (hi)) << 64 | (lo)) + +uint128_t vsl_table[16] = +{ + {.u128 = UINT128(0x0001020304050607, 0x08090A0B0C0D0E0F)}, + {.u128 = UINT128(0x0102030405060708, 0x090A0B0C0D0E0F10)}, + {.u128 = UINT128(0x0203040506070809, 0x0A0B0C0D0E0F1011)}, + {.u128 = UINT128(0x030405060708090A, 0x0B0C0D0E0F101112)}, + {.u128 = UINT128(0x0405060708090A0B, 0x0C0D0E0F10111213)}, + {.u128 = UINT128(0x05060708090A0B0C, 0x0D0E0F1011121314)}, + {.u128 = UINT128(0x060708090A0B0C0D, 0x0E0F101112131415)}, + {.u128 = UINT128(0x0708090A0B0C0D0E, 0x0F10111213141516)}, + {.u128 = UINT128(0x08090A0B0C0D0E0F, 0x1011121314151617)}, + {.u128 = UINT128(0x090A0B0C0D0E0F10, 0x1112131415161718)}, + {.u128 = UINT128(0x0A0B0C0D0E0F1011, 0x1213141516171819)}, + {.u128 = UINT128(0x0B0C0D0E0F101112, 0x131415161718191A)}, + {.u128 = UINT128(0x0C0D0E0F10111213, 0x1415161718191A1B)}, + {.u128 = UINT128(0x0D0E0F1011121314, 0x15161718191A1B1C)}, + {.u128 = UINT128(0x0E0F101112131415, 0x161718191A1B1C1D)}, + {.u128 = UINT128(0x0F10111213141516, 0x1718191A1B1C1D1E)} +}; + +void CPUThread::lvsl(uint32_t instruction) +{ + uint8_t vd = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t sh = 0; + if (ra == 0) + sh = state.regs[rb]; + else + sh = state.regs[ra] + state.regs[rb]; + + sh &= 0xF; + + state.vfr[vd].u128 = vsl_table[sh].u128; + + printf("lvsl v%d,r%d,r%d\n", vd, ra, rb); +} + void CPUThread::subfc(uint32_t instruction) { uint8_t rt = (instruction >> 21) & 0x1F; @@ -404,6 +629,60 @@ void CPUThread::lwzx(uint32_t instruction) printf("lwzx r%d, r%d(r%d)\n", rt, ra, rb); } +void CPUThread::slw(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + bool rc = instruction & 1; + + if ((state.regs[rb] >> 6) & 1) + state.regs[ra] = 0; + else + state.regs[ra] = (uint32_t)state.regs[rs] << (state.regs[rb] & 0x3F); + if (rc) + state.UpdateCRn(state.regs[ra], 0, 0); + + printf("slw r%d,r%d,r%d\n", rs,ra,rb); +} + +void CPUThread::cntlzw(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + + uint32_t s = state.regs[rs]; + int n = 0; + while (n < 32) + { + if ((s >> n) & 1) break; + n++; + } + + printf("cntlzw r%d,r%d (%d)\n", rs, ra, n); + + state.regs[ra] = n; + if (instruction & 1) + state.UpdateCRn(state.regs[ra], 0, 0); +} + +void CPUThread::sld(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + bool rc = instruction & 1; + + if ((state.regs[rb] >> 6) & 1) + state.regs[ra] = 0; + else + state.regs[ra] = state.regs[rs] << (state.regs[rb] & 0x3F); + if (rc) + state.UpdateCRn(state.regs[ra], 0, 0); + + printf("sld r%d,r%d,r%d\n", rs,ra,rb); +} + void CPUThread::and_(uint32_t instruction) { uint8_t rs = (instruction >> 21) & 0x1F; @@ -483,6 +762,58 @@ void CPUThread::mfmsr(uint32_t instruction) printf("mfmsr r%d\n", rt); } +void CPUThread::lbzx(uint32_t instruction) +{ + uint8_t rd = ((instruction >> 21) & 0x1F); + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + state.regs[rd] = Memory::Read8(ea); + + printf("lbzx v%d, r%d, r%d\n", rd, ra, rb); +} + +void CPUThread::neg(uint32_t instruction) +{ + uint8_t rt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + + if ((int64_t)state.regs[ra] == INT64_MIN) + { + state.regs[rt] = state.regs[ra]; + } + else + { + state.regs[rt] = ~state.regs[ra] + 1; + } + + printf("neg r%d,r%d\n", rt, ra); + + if (instruction & 1) + state.UpdateCRn(state.regs[rt], 0, 0); +} + +void CPUThread::nor(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + bool rc = (instruction & 1); + + state.regs[ra] = ~(state.regs[rs] | state.regs[rb]); + + if (rc) + state.UpdateCRn(state.regs[ra], 0L, 0); + + printf("nor%s r%d,r%d,r%d (0x%08x)\n", rc ? "." : "", ra, rs, rb, state.regs[ra]); +} + void CPUThread::subfe(uint32_t instruction) { uint8_t rt = (instruction >> 21) & 0x1F; @@ -490,20 +821,39 @@ void CPUThread::subfe(uint32_t instruction) uint8_t rb = (instruction >> 11) & 0x1F; bool oe = (instruction >> 10) & 1; bool rc = instruction & 1; + + uint32_t v1 = (uint32_t)(~state.regs[ra]); + uint32_t v2 = (uint32_t)(state.regs[rb]); + uint32_t v3 = state.xer.ca; + state.xer.ca = ((v1+v2+v3) < v3) | ((v1+v2) < v1); state.regs[rt] = ~state.regs[ra] + state.regs[rb] + state.xer.ca; if (rc) state.UpdateCRn(state.regs[rt], 0, 0); - uint32_t v1 = (uint32_t)(~state.regs[ra]); - uint32_t v2 = (uint32_t)(state.regs[rb]); - uint32_t v3 = state.xer.ca; - state.xer.ca = ((v1+v2+v3) < v3) | ((v1+v2) < v1); + printf("subfe r%d,r%d,r%d\n", rt, ra, rb); } +void CPUThread::stdx(uint32_t instruction) +{ + uint8_t rt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + Memory::Write64(ea, state.regs[rt]); + + printf("stdx r%d, r%d(r%d)\n", rt, ra, rb); +} + void CPUThread::stwcx(uint32_t instruction) { assert(instruction & 1); @@ -639,6 +989,21 @@ void CPUThread::dcbt(uint32_t instruction) printf("dcbt\n"); } +void CPUThread::xor_(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + bool rc = (instruction & 1); + + state.regs[ra] = state.regs[rs] ^ state.regs[rb]; + + if (rc) + state.UpdateCRn(state.regs[ra], 0L, 0); + + printf("xor%s r%d,r%d,r%d (0x%08x)\n", rc ? "." : "", ra, rs, rb, state.regs[ra]); +} + void CPUThread::or_(uint32_t instruction) { uint8_t rs = (instruction >> 21) & 0x1F; @@ -696,6 +1061,74 @@ void CPUThread::mtspr(uint32_t instruction) } } +void CPUThread::divd(uint32_t instruction) +{ + uint8_t rt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + state.regs[rt] = (int64_t)state.regs[ra] / (int64_t)state.regs[rb]; + + printf("divd r%d,r%d,r%d\n", rt, ra, rb); +} + +void CPUThread::stvlx(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea = 0; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + printf("stvlx v%d,r%d,r%d\n", rs, ra, rb); + + uint32_t tail = ea & 15; + for (int i = 0; i < 16 - tail; i++) + Memory::Write8(ea+i, state.vfr[rs].u8[i]); +} + +void CPUThread::stwbrx(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea = 0; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + printf("stwbrx v%d,r%d,r%d\n", rs, ra, rb); + + Memory::Write32(ea, bswap32(state.regs[rs])); + + printf("stwbrx r%d,r%d,r%d\n", rs, ra, rb); +} + +void CPUThread::stvrx(uint32_t instruction) +{ + uint8_t rs = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea = 0; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + printf("stvrx v%d,r%d,r%d\n", rs, ra, rb); + + uint32_t tail = ea & 15; + for (int i = 15; i > 15 - tail; i--) + Memory::Write8(ea+i, state.vfr[rs].u8[i]); +} + void CPUThread::srawi(uint32_t instruction) { uint8_t rs = (instruction >> 21) & 0x1F; @@ -712,6 +1145,22 @@ void CPUThread::srawi(uint32_t instruction) printf("srawi r%d,r%d,%d\n", ra, rs, sh); } +void CPUThread::dcbz(uint32_t instruction) +{ + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea = 0; + if (ra) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + memset(Memory::GetRawPtrForAddr(ea), 0, 0x80); + + printf("dcbz r%d,r%d\n", ra, rb); +} + void CPUThread::mfspr(uint32_t instruction) { uint8_t rt = (instruction >> 21) & 0x1F; @@ -739,6 +1188,23 @@ void CPUThread::mftb(uint32_t instruction) printf("mftb r%d\n", rt); } +void CPUThread::sthx(uint32_t instruction) +{ + uint8_t rs = ((instruction >> 21) & 0x1F); + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rb = (instruction >> 11) & 0x1F; + + uint32_t ea; + if (ra == 0) + ea = state.regs[rb]; + else + ea = state.regs[ra] + state.regs[rb]; + + Memory::Write16(ea, state.regs[rs]); + + printf("sthx r%d,r%d,r%d\n", rs, ra, rb); +} + void CPUThread::lwz(uint32_t instruction) { int64_t ds = (int64_t)(int16_t)(instruction & 0xFFFF); @@ -756,6 +1222,24 @@ void CPUThread::lwz(uint32_t instruction) state.regs[rs] = Memory::Read32(ea); } +void CPUThread::lwzu(uint32_t instruction) +{ + int64_t ds = (int64_t)(int16_t)(instruction & 0xFFFF); + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rs = (instruction >> 21) & 0x1F; + + uint32_t ea = 0; + if (ra == 0) + ea = ds; + else + ea = state.regs[ra] + ds; + + printf("lwzu r%d, %ld(r%d)\n", rs, ds, ra); + + state.regs[rs] = Memory::Read32(ea); + state.regs[ra] = ea; +} + void CPUThread::lbz(uint32_t instruction) { int16_t ds = (int16_t)(instruction & 0xFFFF); @@ -773,6 +1257,20 @@ void CPUThread::lbz(uint32_t instruction) state.regs[rs] = Memory::Read8(ea); } +void CPUThread::lbzu(uint32_t instruction) +{ + int16_t ds = (int16_t)(instruction & 0xFFFF); + uint8_t ra = (instruction >> 16) & 0x1F; + uint8_t rs = (instruction >> 21) & 0x1F; + + uint32_t ea = state.regs[ra] + ds; + + printf("lbzu r%d, %d(r%d)\n", rs, ds, ra); + + state.regs[rs] = Memory::Read8(ea); + state.regs[ra] = ea; +} + void CPUThread::stw(uint32_t instruction) { int16_t ds = (int16_t)(instruction & 0xFFFF); @@ -824,6 +1322,21 @@ void CPUThread::stb(uint32_t instruction) printf("stb r%d, %d(r%d)\n", rt, ds, ra); } +void CPUThread::stbu(uint32_t instruction) +{ + uint8_t rt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + int16_t ds = instruction & 0xFFFC; + + uint32_t ea = state.regs[ra] + ds; + + Memory::Write8(ea, state.regs[rt]); + + state.regs[ra] = ea; + + printf("stbu r%d, %d(r%d)\n", rt, ds, ra); +} + void CPUThread::lhz(uint32_t instruction) { uint8_t rt = (instruction >> 21) & 0x1F; @@ -870,11 +1383,28 @@ void CPUThread::lfs(uint32_t instruction) else ea = state.regs[ra] + ds; - state.fr[frt].u = Memory::Read32(ea); + state.fr[frt].d = std::bit_cast(Memory::Read32(ea)); printf("lfs fr%d, %d(r%d)\n", frt, ds, ra); } +void CPUThread::lfd(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + int16_t ds = instruction & 0xFFFC; + + uint32_t ea; + if (!ra) + ea = (int32_t)ds; + else + ea = state.regs[ra] + ds; + + state.fr[frt].u = Memory::Read64(ea); + + printf("lfd fr%d, %d(r%d) (%f, 0x%08lx)\n", frt, ds, ra, state.fr[frt].d, state.fr[frt].u); +} + void CPUThread::stfs(uint32_t instruction) { uint8_t frt = (instruction >> 21) & 0x1F; @@ -892,6 +1422,23 @@ void CPUThread::stfs(uint32_t instruction) printf("stfs fr%d, %d(r%d)\n", frt, ds, ra); } +void CPUThread::stfd(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t ra = (instruction >> 16) & 0x1F; + int16_t ds = instruction & 0xFFFC; + + uint32_t ea; + if (!ra) + ea = (int32_t)ds; + else + ea = state.regs[ra] + ds; + + Memory::Write64(ea, state.fr[frt].u); + + printf("stfd fr%d, %d(r%d)\n", frt, ds, ra); +} + void CPUThread::ld(uint32_t instruction) { uint8_t rt = (instruction >> 21) & 0x1F; @@ -913,6 +1460,21 @@ void CPUThread::ld(uint32_t instruction) state.regs[ra] = ea; } +void CPUThread::fmuls(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t fra = (instruction >> 16) & 0x1F; + uint8_t frc = (instruction >> 6) & 0x1F; + bool rc = instruction & 1; + + state.fr[frt].d = (double)((float)state.fr[fra].d * (float)state.fr[frc].d); + + if (rc) + state.UpdateCRn(state.fr[frt].d, 0, 0); + + printf("fmuls f%d,f%d,f%d\n", frt, fra, frc); +} + void CPUThread::std(uint32_t instruction) { int16_t ds = (int16_t)(instruction & 0xFFFC); @@ -933,3 +1495,92 @@ void CPUThread::std(uint32_t instruction) if (update) state.regs[ra] = ea; } + +void CPUThread::fcmpu(uint32_t instruction) +{ + uint8_t bf = (instruction >> 23) & 0x7; + uint8_t fra = (instruction >> 16) & 0x1F; + uint8_t frb = (instruction >> 11) & 0x1F; + + double& a = state.fr[fra].d; + double& b = state.fr[frb].d; + + if (std::isnan(a) || std::isnan(b)) + state.SetCR(0, 1); + else + state.UpdateCRn(a, b, 0); + + printf("fcmpu cr%d,f%d,f%d\n", bf, fra, frb); +} + +void CPUThread::frsp(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t frb = (instruction >> 11) & 0x1F; + + state.fr[frt].d = (double)(float)state.fr[frb].d; + + printf("frsp f%d,f%d\n", frt, frb); +} + +void CPUThread::fdiv(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t fra = (instruction >> 16) & 0x1F; + uint8_t frb = (instruction >> 11) & 0x1F; + + state.fr[frt].d = state.fr[fra].d / state.fr[frb].d; + + printf("fdiv f%d,f%d,f%d\n", frt, fra, frb); +} + +void CPUThread::fsqrt(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t frb = (instruction >> 11) & 0x1F; + + state.fr[frt].d = sqrt(state.fr[frb].d); + + printf("fsqrt f%d,f%d\n", frt, frb); +} + +void CPUThread::fmul(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t frb = (instruction >> 11) & 0x1F; + uint8_t frc = (instruction >> 6) & 0x1F; + + state.fr[frt].d = state.fr[frb].d * state.fr[frc].d; + + printf("fmul f%d,f%d,f%d\n", frt, frb, frc); +} + +void CPUThread::fctid(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t frb = (instruction >> 11) & 0x1F; + + double d = state.fr[frb].d; + if (std::isnan(d)) + { + state.fr[frt].u = 0x8000000000000000u; + } + else + { + state.fr[frt].u = (int64_t)d; + } + + printf("fctid f%d,f%d (0x%08lx)\n", frt, frb); +} + +void CPUThread::fcfid(uint32_t instruction) +{ + uint8_t frt = (instruction >> 21) & 0x1F; + uint8_t frb = (instruction >> 11) & 0x1F; + + uint64_t u = state.fr[frb].u; + + state.fr[frt].d = (double)(int64_t)u; + + printf("fcfid f%d,f%d (%f)\n", frt, frb, state.fr[frt].d); +} diff --git a/src/kernel/modules/xboxkrnl.cpp b/src/kernel/modules/xboxkrnl.cpp index 18b4926..cf22311 100644 --- a/src/kernel/modules/xboxkrnl.cpp +++ b/src/kernel/modules/xboxkrnl.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -59,21 +60,63 @@ void XboxKrnlModule::CallFunctionByOrdinal(uint32_t ordinal, CPUThread &caller) case 0x10: ExGetXConfigSetting(caller); return; + case 0x11: + ExInitializeReadWriteLock(caller); + return; case 0x15: ExRegisterTitleTerminationNotification(caller); return; + case 0x4D: + KeAcquireSpinLockAtRaisedIrql(caller); + return; + case 0x5F: + KeEnterCriticalRegion(caller); + return; case 0x66: KeGetCurrentProcessType(caller); return; + case 0x6F: + KeInitializeDPC(caller); + return; + case 0x74: + KeInitializeSemaphore(caller); + return; + case 0x75: + KeInitializeTimerEx(caller); + return; + case 0x7D: + KeLeaveCriticalRegion(caller); + return; + case 0x83: + KeQueryPerformanceCounter(caller); + return; case 0x84: KeQuerySystemTime(caller); return; + case 0x85: + KeRaiseIrqlToDPC(caller); + return; + case 0x89: + KeLeaveCriticalRegion(caller); + return; + case 0xB4: + KfReleaseSpinLock(caller); + return; + case 0xBA: + MmAllocatePhysicalMemory(caller); + return; + case 0xc5: + MmQueryAllocationSize(caller); + return; case 0xcc: NtAllocateVirtualMemory(caller); return; case 0xd2: NtCreateFile(caller); return; + case 0xe7: + NtQueryFullAttributesFile(caller); + return; case 0xee: NtQueryVirtualMemory(caller); return; @@ -98,6 +141,12 @@ void XboxKrnlModule::CallFunctionByOrdinal(uint32_t ordinal, CPUThread &caller) case 0x140: RltTimeToTimeFields(caller); return; + case 0x152: + KeAllocTLS(caller); + return; + case 0x154: + KeTlsGetValue(caller); + return; case 0x195: XexGetModuleHandle(caller); return; @@ -106,9 +155,21 @@ void XboxKrnlModule::CallFunctionByOrdinal(uint32_t ordinal, CPUThread &caller) return; case 0x1AA: KE_UNIMPLEMENTED("ExDebugMonitorServices"); + case 0x20D: + KE_UNIMPLEMENTED("DrvSetUserBindingCallback"); + case 0x28F: + KE_UNIMPLEMENTED("DrvSetDeviceConfigChangeCallback"); + case 0x328: + KE_UNIMPLEMENTED("DrvSetMicArrayStartCallback"); + case 0x35B: + KE_UNIMPLEMENTED("DrvSetAudioLatencyCallback"); + case 0x386: + KE_UNIMPLEMENTED("XInputdSetFailedConnectionOrBindCallback"); case 0x28A: NtAllocateEncryptedMemory(caller); return; + case 0x334: + KE_UNIMPLEMENTED("EtxProducerRegister"); } printf("Unknown xboxkrnl.exe function called with ordinal 0x%08x\n", ordinal); @@ -117,6 +178,51 @@ void XboxKrnlModule::CallFunctionByOrdinal(uint32_t ordinal, CPUThread &caller) std::ofstream debug_log; +bool XboxKrnlModule::IsExportVariable(uint32_t ordinal) +{ + switch (ordinal) + { + case 0x0000000C: + case 0x0000000E: + case 0x00000012: + case 0x00000017: + case 0x0000001B: + case 0x0000001C: + case 0x00000036: + case 0x0000003A: + case 0x0000003E: + case 0x00000059: + case 0x000000AD: + case 0x000000B5: + case 0x000000CB: + case 0x00000106: + case 0x00000112: + case 0x00000156: + case 0x00000157: + case 0x00000158: + case 0x00000193: + case 0x000001AE: + case 0x000001AF: + case 0x000001BE: + case 0x000001BF: + case 0x000001C0: + case 0x000001C1: + case 0x0000025C: + case 0x00000266: + case 0x0000026B: + case 0x0000026D: + case 0x0000026E: + case 0x000002AB: + case 0x000002DB: + case 0x000002DC: + case 0x000002F1: + case 0x00000345: + return true; + } + + return false; +} + void XboxKrnlModule::DbgPrint(CPUThread &caller) { if (!debug_log.is_open()) @@ -188,7 +294,7 @@ void XboxKrnlModule::ExAllocatePoolWithTag(CPUThread &caller) printf("ExAllocatePoolWithTag(0x%08lx, 0x%08x)\n", size, tag); - caller.GetState().regs[3] = Memory::VirtAllocMemoryRange(0xE0000000, 0xFFD00000, size); + caller.GetState().regs[3] = Memory::VirtAllocMemoryRange(0x3A000000, 0x3FBEFFFF, size); Memory::AllocMemory(caller.GetState().regs[3], size); } @@ -220,6 +326,18 @@ void XboxKrnlModule::ExGetXConfigSetting(CPUThread &caller) printf("ExGetXConfigSetting(%d, %d, 0x%08x, %d, 0x%08x)\n", category, setting, bufPtr, bufSize, reqSizePtr); } +void XboxKrnlModule::ExInitializeReadWriteLock(CPUThread &caller) +{ + uint32_t outPtr = caller.GetState().regs[3]; + + Memory::Write32(outPtr+0x00, (uint32_t)-1); + Memory::Write32(outPtr+0x04, 0); + Memory::Write32(outPtr+0x08, 0); + Memory::Write32(outPtr+0x0C, 0); + + printf("ExInitializeReadWriteLock(0x%08x)\n", outPtr); +} + void XboxKrnlModule::ExRegisterTitleTerminationNotification(CPUThread &caller) { uint32_t terminationStructPtr = caller.GetState().regs[3]; @@ -237,12 +355,87 @@ void XboxKrnlModule::ExRegisterTitleTerminationNotification(CPUThread &caller) printf("ExRegisterTitleTerminationNotification(0x%08x, %d)\n", terminationStructPtr, create); } +void XboxKrnlModule::KeAcquireSpinLockAtRaisedIrql(CPUThread &caller) +{ + printf("KeAcquireSpinLockAtRaisedIrql(0x%08x)\n", caller.GetState().regs[3]); +} + +void XboxKrnlModule::KeEnterCriticalRegion(CPUThread &caller) +{ + printf("KeEnterCriticalRegion()\n"); +} + void XboxKrnlModule::KeGetCurrentProcessType(CPUThread &caller) { printf("KeGetCurrentProcessType()\n"); caller.GetState().regs[3] = 2; } +void XboxKrnlModule::KeInitializeDPC(CPUThread &caller) +{ + uint32_t dpcPtr = caller.GetState().regs[3]; + uint32_t routine = caller.GetState().regs[4]; + uint32_t context = caller.GetState().regs[5]; + + Memory::Write16(dpcPtr+0x00, 19); // type + Memory::Write8(dpcPtr+0x02, 0); // selected CPU + Memory::Write8(dpcPtr+0x03, 0); // desired CPU + Memory::Write32(dpcPtr+0x04, 0); // list_entry.flink + Memory::Write32(dpcPtr+0x08, 0); // list_entry.blink + Memory::Write32(dpcPtr+0x0C, routine); + Memory::Write32(dpcPtr+0x10, context); + Memory::Write32(dpcPtr+0x14, 0); + Memory::Write32(dpcPtr+0x18, 0); + + printf("KeInitializeDPC(0x%08x, 0x%08x, 0x%08x)\n", dpcPtr, routine, context); +} + +void XboxKrnlModule::KeInitializeSemaphore(CPUThread &caller) +{ + uint32_t ptr = caller.GetState().regs[3]; + uint32_t count = caller.GetState().regs[4]; + uint32_t limit = caller.GetState().regs[5]; + + Memory::Write8(ptr+0x00, 5); + Memory::Write8(ptr+0x01, 0); + Memory::Write8(ptr+0x02, 0); + Memory::Write8(ptr+0x03, 0); + Memory::Write32(ptr+0x04, count); + Memory::Write32(ptr+0x08, 0); + Memory::Write32(ptr+0x0C, 0); + Memory::Write32(ptr+0x10, limit); + + printf("KeInitializeSemaphore(0x%08x,%d,%d)\n", ptr, count, limit); +} + +void XboxKrnlModule::KeInitializeTimerEx(CPUThread &caller) +{ + uint32_t timerPtr = caller.GetState().regs[3]; + uint32_t type = caller.GetState().regs[4]; + uint32_t processType = caller.GetState().regs[5]; + + Memory::Write8(timerPtr+0x00, type+8); + Memory::Write8(timerPtr+0x02, processType); + Memory::Write8(timerPtr+0x03, 0); + Memory::Write32(timerPtr+0x04, 0); + // Offset 0x10: KTIMER + Memory::Write64(timerPtr+0x10, 0); + Memory::Write32(timerPtr+0x34, 0); + + printf("KeInitializeTimerEx(0x%08x)\n", timerPtr); +} + +void XboxKrnlModule::KeLeaveCriticalRegion(CPUThread &caller) +{ + printf("KeLeaveCriticalRegion()\n"); +} + +void XboxKrnlModule::KeQueryPerformanceCounter(CPUThread &caller) +{ + printf("KeQueryPerformanceCounter()\n"); + caller.GetState().regs[3] = 50000000; +} + void XboxKrnlModule::KeQuerySystemTime(CPUThread &caller) { uint32_t timePtr = caller.GetState().regs[3]; @@ -250,15 +443,69 @@ void XboxKrnlModule::KeQuerySystemTime(CPUThread &caller) Memory::Write64(timePtr, time(NULL)); } +void XboxKrnlModule::KeRaiseIrqlToDPC(CPUThread &caller) +{ + uint8_t old_irql = Memory::Read8(caller.GetState().pcr_address+0x18); + Memory::Write8(caller.GetState().pcr_address+0x18, 2); + caller.GetState().regs[3] = old_irql; + printf("KeRaiseIrqlToDPC()\n"); +} + +void XboxKrnlModule::KeReleaseSpinLockAtRaisedIrql(CPUThread &caller) +{ + printf("KeReleaseSpinLockAtRaisedIrql(0x%08x)\n", caller.GetState().regs[3]); +} + +void XboxKrnlModule::KfReleaseSpinLock(CPUThread &caller) +{ + printf("KfReleaseSpinLock(0x%08x)\n", caller.GetState().regs[3]); + Memory::Write8(caller.GetState().pcr_address+0x18, (uint8_t)caller.GetState().regs[4]); +} + +void XboxKrnlModule::MmAllocatePhysicalMemory(CPUThread &caller) +{ + int flags = caller.GetState().regs[3]; + uint32_t region_size = caller.GetState().regs[4]; + + uint32_t base = Memory::VirtAllocMemoryRange(0xE0000000, 0xFFD00000, region_size); + Memory::AllocMemory(base, region_size); + + caller.GetState().regs[3] = base; + + printf("MmAllocatePhysicalMemory(%d, 0x%08x, 0x%x, 0x%08x, 0x%08x, 0x%08x)\n", + flags, (uint32_t)caller.GetState().regs[4], + (uint32_t)caller.GetState().regs[5], (uint32_t)caller.GetState().regs[6], + (uint32_t)caller.GetState().regs[7], (uint32_t)caller.GetState().regs[8]); +} + +void XboxKrnlModule::MmQueryAllocationSize(CPUThread &caller) +{ + uint32_t base = caller.GetState().regs[3]; + AllocInfo info; + uint32_t size = 0; + if (Memory::GetAllocInfo(base, info)) + size = info.regionSize; + caller.GetState().regs[3] = size; + printf("MmQueryAllocationSize(0x%08x)\n", base); +} + // TODO: Make this not suck void XboxKrnlModule::NtAllocateVirtualMemory(CPUThread &caller) { uint32_t region_size_ptr = caller.GetState().regs[4]; uint32_t out_addr_ptr = caller.GetState().regs[3]; + uint32_t alloc_type = caller.GetState().regs[5]; uint32_t region_size = Memory::Read32(region_size_ptr); - uint32_t addr = Memory::VirtAllocMemoryRange(0x8C000000, 0x8FFFFFFF, region_size); + uint32_t addr = Memory::Read32(out_addr_ptr); + if (!addr) + { + if (alloc_type & 0x20000000) + addr = Memory::VirtAllocMemoryRange(0x7A000000, 0x7EFFFFFF, region_size); + else + addr = Memory::VirtAllocMemoryRange(0x20000000, 0x2FFFFFFF, region_size); + } Memory::AllocMemory(addr, region_size); Memory::Write32(out_addr_ptr, addr); @@ -292,6 +539,29 @@ void XboxKrnlModule::NtCreateFile(CPUThread &caller) caller.GetState().regs[3] = (uint32_t)-1U; } +void XboxKrnlModule::NtQueryFullAttributesFile(CPUThread &caller) +{ + arg_index = 0; + uint32_t objAttrPtr = GetNextArg(caller.GetState()); + uint32_t openInfo = GetNextArg(caller.GetState()); + + uint32_t ansiPtr = Memory::Read32(objAttrPtr+0x4); + uint32_t namePtr = Memory::Read32(ansiPtr+0x04); + + std::string name = (char*)Memory::GetRawPtrForAddr(namePtr); + + printf("NtQueryFullAttributesFile(\"%s\" (0x%08x), 0x%08x)\n", name.c_str(), objAttrPtr, openInfo); + + FileHandle_t handle = VFS::OpenFile(name, OPENMODE_READ | OPENMODE_BINARY); + if (handle == FILE_INVALID_HANDLE) + { + caller.GetState().regs[3] = 0xC000000FL; + return; + } + + assert(0); +} + void XboxKrnlModule::NtQueryVirtualMemory(CPUThread &caller) { arg_index = 0; @@ -332,7 +602,7 @@ void XboxKrnlModule::RtlEnterCriticalSection(CPUThread &caller) { // TODO: Recursive locks and waiting on locks printf("Critical section already entered!\n"); - exit(1); + // exit(1); } Memory::Write64(critPtr, 1); @@ -483,6 +753,23 @@ void XboxKrnlModule::RltTimeToTimeFields(CPUThread &caller) printf("RltTimeToTimeFields(0x%08x, 0x%08x)\n", timePtr, outPtr); } +void XboxKrnlModule::KeAllocTLS(CPUThread &caller) +{ + uint32_t addr = caller.GetState().tls_lowest_alloced; + caller.GetState().tls_lowest_alloced += 0x80; + + printf("KeAllocTLS()\n"); + + caller.GetState().regs[3] = addr / 0x80; +} + +void XboxKrnlModule::KeTlsGetValue(CPUThread &caller) +{ + uint32_t slot = caller.GetState().regs[3]; + caller.GetState().regs[3] = Memory::Read32(caller.GetState().tls_addr+(slot-1)*0x80); + printf("KeTlsGetValue(%d)\n", slot); +} + void XboxKrnlModule::XexGetModuleHandle(CPUThread &caller) { arg_index = 0; diff --git a/src/kernel/modules/xboxkrnl.h b/src/kernel/modules/xboxkrnl.h index d85a800..b25f7f1 100644 --- a/src/kernel/modules/xboxkrnl.h +++ b/src/kernel/modules/xboxkrnl.h @@ -11,15 +11,31 @@ class XboxKrnlModule : public IModule void CallFunctionByOrdinal(uint32_t ordinal, CPUThread& caller); virtual uint32_t GetHandle() const {return modHandle;} + + bool IsExportVariable(uint32_t ordinal); private: void DbgPrint(CPUThread& caller); // 0x03 void ExAllocatePoolWithTag(CPUThread& caller); // 0x0A void ExGetXConfigSetting(CPUThread& caller); // 0x10 + void ExInitializeReadWriteLock(CPUThread& caller); // 0x11 void ExRegisterTitleTerminationNotification(CPUThread& caller); // 0x15 + void KeAcquireSpinLockAtRaisedIrql(CPUThread& caller); // 0x4D + void KeEnterCriticalRegion(CPUThread& caller); // 0x5F void KeGetCurrentProcessType(CPUThread& caller); // 0x66 + void KeInitializeDPC(CPUThread& caller); // 0x6F + void KeInitializeSemaphore(CPUThread& caller); // 0x74 + void KeInitializeTimerEx(CPUThread& caller); // 0x75 + void KeLeaveCriticalRegion(CPUThread& caller); // 0x7D + void KeQueryPerformanceCounter(CPUThread& caller); // 0x83 void KeQuerySystemTime(CPUThread& caller); // 0x84 - void NtAllocateVirtualMemory(CPUThread& caller); + void KeRaiseIrqlToDPC(CPUThread& caller); // 0x85 + void KeReleaseSpinLockAtRaisedIrql(CPUThread& caller); // 0x89 + void KfReleaseSpinLock(CPUThread& caller); // 0xb4 + void MmAllocatePhysicalMemory(CPUThread& caller); // 0xba + void MmQueryAllocationSize(CPUThread& caller); // 0xc5 + void NtAllocateVirtualMemory(CPUThread& caller); // 0xcc void NtCreateFile(CPUThread& caller); // 0xd2 + void NtQueryFullAttributesFile(CPUThread& caller); // 0xe7 void NtQueryVirtualMemory(CPUThread& caller); // 0xee void ObTranslateSymbolicLink(CPUThread& caller); // 0x113 void RtlEnterCriticalSection(CPUThread& caller); // 0x125 @@ -28,6 +44,8 @@ class XboxKrnlModule : public IModule void RtlLeaveCriticalSection(CPUThread& caller); // 0x130 void _snprintf(CPUThread& caller); // 0x13A void RltTimeToTimeFields(CPUThread& caller); // 0x140 + void KeAllocTLS(CPUThread& caller); // 0x152 + void KeTlsGetValue(CPUThread& caller); // 0x154 void XexGetModuleHandle(CPUThread& caller); // 0x195 // Warning: This will modify CPU state by calling the new module's entrypoint! void XexLoadImage(CPUThread& caller); // 0x199 diff --git a/src/loader/xex.cpp b/src/loader/xex.cpp index 208eb51..643df0e 100644 --- a/src/loader/xex.cpp +++ b/src/loader/xex.cpp @@ -10,6 +10,7 @@ #include #include #include +#include static uint32_t handle = 0x10000001; @@ -307,6 +308,13 @@ void XexLoader::ParseLibraryInfo(uint32_t offset, xexLibrary_t &lib, int index, Memory::Write32(recordAddr+0x08, 0x7D6903A6); Memory::Write32(recordAddr+0x0C, 0x4E800420); } + else + { + if (name == "xboxkrnl.exe" && krnlModule.IsExportVariable(record & 0xFFFF)) + { + printf("TODO: Variable import 0x%08x\n", record); + } + } } } diff --git a/src/main.cpp b/src/main.cpp index 606f22b..b8a231e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,11 @@ void atexit_handler() int main(int argc, char** argv) { VFS::SetRootDirectory(".waternoose"); - VFS::MountDirectory("/SystemRoot", "SystemRoot"); + VFS::MountDirectory("/SystemRoot", "systemroot"); + VFS::MountDirectory("/Device/Flash", "flash"); + VFS::MountDirectory("/Device/Harddisk0/Partition0", "drv0p0"); + VFS::MountDirectory("/Device/Harddisk0/Partition1", "drv0p1"); + VFS::MountDirectory("/Device/Cdrom0", "cdrom"); if (argc < 2) { @@ -29,7 +33,9 @@ int main(int argc, char** argv) char* xam_buf; size_t xam_size; - std::ifstream file(".waternoose/SystemRoot/xam.xex", std::ios::binary | std::ios::ate); + std::ifstream file(".waternoose/systemroot/xam.xex", std::ios::binary | std::ios::ate); + if (!file.is_open()) + printf("Failed to open file %s\n", ".waternoose/systemroot/xam.xex"); xam_size = file.tellg(); file.seekg(0, std::ios::beg); xam_buf = new char[xam_size]; @@ -53,7 +59,7 @@ int main(int argc, char** argv) std::atexit(Memory::Dump); xam = new XexLoader((uint8_t*)xam_buf, xam_size, ".waternoose/SystemRoot/xam.xex"); - XexLoader loader((uint8_t*)buf, size, argv[0]); + //XexLoader loader((uint8_t*)buf, size, argv[0]); #if 1 mainThreadStackSize = xam->GetStackSize(); diff --git a/src/memory/memory.cpp b/src/memory/memory.cpp index 810b280..0be4e9a 100644 --- a/src/memory/memory.cpp +++ b/src/memory/memory.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "memory.h" extern uint32_t mainThreadStackSize; @@ -26,6 +27,9 @@ void Memory::Initialize() readPages = new uint8_t*[MAX_ADDRESS_SPACE / PAGE_SIZE]; writePages = new uint8_t*[MAX_ADDRESS_SPACE / PAGE_SIZE]; usedPages.reset(); + + for (size_t i = 0; i < 64*1024; i += 4096) + usedPages[i / 4096] = true; } void Memory::Dump() @@ -138,15 +142,28 @@ bool Memory::GetAllocInfo(uint32_t addr, AllocInfo& outInfo) return false; } -uint8_t Memory::Read8(uint32_t addr) +uint8_t Memory::Read8(uint32_t addr, bool slow) { - if (!readPages[addr / PAGE_SIZE]) + if (!slow) { - printf("Read8 from unmapped addr 0x%08x\n", addr); - exit(1); - } + if (!readPages[addr / PAGE_SIZE]) + { + return Read8(addr, true); + } - return readPages[addr / PAGE_SIZE][addr % PAGE_SIZE]; + return readPages[addr / PAGE_SIZE][addr % PAGE_SIZE]; + } + else + { + switch (addr) + { + case 0x15A: + return 0x06; + default: + printf("Read8 from unmapped addr 0x%08x\n", addr); + exit(1); + } + } } uint16_t Memory::Read16(uint32_t addr, bool slow) @@ -165,11 +182,11 @@ uint16_t Memory::Read16(uint32_t addr, bool slow) switch (addr) { case 0x10158: - return 0x2; // Some kind of console type (maybe debug vs retail?). xbdm.xex relies on this while booting + return bswap16(0x2); // Some kind of console type (maybe debug vs retail?). xbdm.xex relies on this while booting case 0x1015A: return 0; // More xbdm.xex nonsense case 0x1015C: - return 0x4f80; // According to assert messages inside xbdm, this is the console's firmware revision + return bswap16(0x4f80); // According to assert messages inside xbdm, this is the console's firmware revision case 0x1015E: return 0; // Setting this to 0x8000 will cause a bunch of extra stuff to happen inside xbdm case 0x10164: @@ -197,10 +214,17 @@ uint32_t Memory::Read32(uint32_t addr, bool slow) } else { + static int system_ticks = 0; switch (addr) { + case 0x59: + return 0; + case 0xbd: + return bswap32(system_ticks++); + case 0x156: + return bswap32(0x20); case 0x10156: - return 0x2000000; // Setting this to 0x2000000 causes some kind of memory address to be set to 1 + return bswap32(0x2000000); // Setting this to 0x2000000 causes some kind of memory address to be set to 1 default: printf("Read32 from unmapped address 0x%08x\n", addr); exit(1); @@ -219,6 +243,28 @@ uint64_t Memory::Read64(uint32_t addr) return bswap64(*(uint64_t*)&readPages[addr / PAGE_SIZE][addr % PAGE_SIZE]); } +unsigned __int128 swap(unsigned __int128 n) +{ + unsigned __int128 m; + const uint64_t* src = (const uint64_t*)(&n); + uint64_t* dest = (uint64_t*)(&m); + dest[1] = bswap64(src[0]); + dest[0] = bswap64(src[1]); + return m; +} + +__uint128_t Memory::Read128(uint32_t addr) +{ + if (!readPages[addr / PAGE_SIZE]) + { + printf("Read128 from unmapped addr 0x%08x\n", addr); + exit(1); + } + + __uint128_t t = *(__uint128_t*)&readPages[addr / PAGE_SIZE][addr % PAGE_SIZE]; + return swap(t); +} + void Memory::Write8(uint32_t addr, uint8_t data) { if (!writePages[addr / PAGE_SIZE]) @@ -262,3 +308,15 @@ void Memory::Write64(uint32_t addr, uint64_t data) *(uint64_t*)&writePages[addr / PAGE_SIZE][addr % PAGE_SIZE] = bswap64(data); } + +void Memory::Write128(uint32_t addr, __uint128_t data) +{ + if (!writePages[addr / PAGE_SIZE]) + { + printf("Write64 to unmapped addr 0x%08x\n", addr); + exit(1); + } + + data = swap(data); + *(__uint128_t*)&writePages[addr / PAGE_SIZE][addr % PAGE_SIZE] = data; +} diff --git a/src/memory/memory.h b/src/memory/memory.h index b27c440..c30dc72 100644 --- a/src/memory/memory.h +++ b/src/memory/memory.h @@ -31,15 +31,17 @@ uint32_t VirtAllocMemoryRange(uint32_t beginAddr, uint32_t endAddr, uint32_t siz uint8_t* GetRawPtrForAddr(uint32_t addr); bool GetAllocInfo(uint32_t addr, AllocInfo& info); -uint8_t Read8(uint32_t addr); +uint8_t Read8(uint32_t addr, bool slow = false); /// @brief Reads a 16-bit value from the memory mapped to `addr`. Leave slow as default, it's used internally uint16_t Read16(uint32_t addr, bool slow = false); uint32_t Read32(uint32_t addr, bool slow = false); uint64_t Read64(uint32_t addr); +__uint128_t Read128(uint32_t addr); void Write8(uint32_t addr, uint8_t data); void Write16(uint32_t addr, uint16_t data); void Write32(uint32_t addr, uint32_t data); void Write64(uint32_t addr, uint64_t data); +void Write128(uint32_t addr, __uint128_t data); } \ No newline at end of file diff --git a/src/vfs/VFS.cpp b/src/vfs/VFS.cpp index 7fdfd9f..b1f2f12 100644 --- a/src/vfs/VFS.cpp +++ b/src/vfs/VFS.cpp @@ -2,6 +2,10 @@ #include #include +#include +#include +#include +#include std::string rootPath; @@ -23,6 +27,20 @@ bool isPathValid(std::string path) return true; } +void ToLowercase(std::string& str) +{ + std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {return std::tolower(c);}); +} + +void BackslashToForwad(std::string& str) +{ + for (auto& c : str) + { + if (c == '\\') + c = '/'; + } +} + void VFS::SetRootDirectory(std::string rootDir) { // This must be absolute, or else relative to the current directory @@ -35,15 +53,72 @@ void VFS::SetRootDirectory(std::string rootDir) std::filesystem::create_directories(rootPath); } -std::unordered_map mountPoints; +struct MountPoint +{ + std::string mp, path; +}; + +std::vector mountPoints; void VFS::MountDirectory(std::string devicePath, std::string mntPath) { if (!isPathValid(mntPath)) printf("Invalid path: \"%s\"\n", mntPath.c_str()); + + MountPoint mp; + mp.mp = devicePath; + mp.path = mntPath; - mountPoints[devicePath] = mntPath; + mountPoints.push_back(mp); if (!std::filesystem::exists(rootPath + "/" + mntPath)) std::filesystem::create_directory(rootPath + "/" + mntPath); } + +bool FindMountpoint(std::string& path) +{ + for (auto& mnt : mountPoints) + { + if (!strncmp(mnt.mp.c_str(), path.c_str(), mnt.mp.size())) + { + path = path.substr(mnt.mp.size()); + path = rootPath + "/" + mnt.path + path; + return true; + } + } + + return false; +} + +std::unordered_map openFiles; +FileHandle_t curhandle = 1; + +FileHandle_t VFS::OpenFile(std::string path, int openMode) +{ + BackslashToForwad(path); + if (!FindMountpoint(path)) + { + printf("Invalid filepath \"%s\"\n", path.c_str()); + return FILE_INVALID_HANDLE; + } + + std::string perms = ""; + if (openMode & OPENMODE_WRITE) + perms += "w"; + else if (openMode & OPENMODE_READ) + perms += "r"; + if (openMode & OPENMODE_BINARY) + perms += "b"; + + FILE* f = fopen(path.c_str(), perms.c_str()); + if (!f) + { + printf("Failed to open file \"%s\"\n", path.c_str()); + return FILE_INVALID_HANDLE; + } + + FileHandle_t handle = curhandle++; + openFiles[handle] = f; + + return handle; +} diff --git a/src/vfs/VFS.h b/src/vfs/VFS.h index 0252c48..85ffa0f 100644 --- a/src/vfs/VFS.h +++ b/src/vfs/VFS.h @@ -2,6 +2,16 @@ #include +typedef int FileHandle_t; +#define FILE_INVALID_HANDLE (FileHandle_t)-1 + +enum FileOpenMode : int +{ + OPENMODE_READ = 1, + OPENMODE_WRITE = 2, + OPENMODE_BINARY = 4 +}; + namespace VFS { @@ -16,4 +26,6 @@ void SetRootDirectory(std::string rootDir); /// @param mntPath The path on the host, relative to the root directory void MountDirectory(std::string devicePath, std::string mntPath); +FileHandle_t OpenFile(std::string path, int openMode); + } \ No newline at end of file