Homebrew I got a Nintendo 64 emulator running (extremely poorly) on the New 3DS!

Do you think playable N64 emulation on New 3DS is possible?

  • Yes, of course, the New 3DS has a powerful quad-core processor, so it should easily be capable!

  • No way, the New 3DS isn't powerful enough to correctly emulate a 22-year-old console...


Results are only viewable after voting.
Status
Not open for further replies.
D

Deleted User

Guest
A screenshot isn't going to cut it (especially not a direct one since that would be way too easy to fake). Post a video just like the one you did before, but with the bottom screen clearly visible and you showing yourself inputting commands that match the gameplay.

Right now there's nothing telling us this isn't just a video playing. I want to be proven wrong, but that's where we're at.
Precisely this, although a screenshot is better than nothing, as that would take at least some effort to fake.
 

SimonMKWii

Professional Idiot
OP
Member
Joined
Nov 18, 2017
Messages
666
Trophies
0
Location
Melbourne, Victoria
XP
2,760
Country
Australia
bot_0004.png


That's a direct screenshot taken with Luma3DS.

I don't know how to prove it other than to post the .cia file which contains the ROM.

Even then people would call fake just because all the goddamn planets in the fucking solar system aren't perfectly aligned at that precise moment in time.

Do you want me to post the source code?
 
D

Deleted User

Guest
Not really, I once made a little test program that displayed two images on the 3DS top and bottom screen. I'm not even a good programmer
1. That is effort :/
2. Based on what they've already said (and what we've found), a faked image would need to have Mario 64 on the top screen, and a GUI on the bottom screen, in the format of the Moflex player, with:
- Filename (minus extension)
- File Size
- Framerate / Frametime info
- Polygons on screen
- VI refresh rate
- Current button inputs / mapped inputs
Creating such an image would be no easy task.
 
D

Deleted User

Guest
bot_0004.png


That's a direct screenshot taken with Luma3DS.

I don't know how to prove it other than to post the .cia file which contains the ROM.

Even then people would call fake just because all the goddamn planets in the fucking solar system aren't perfectly aligned at that precise moment in time.

Do you want me to post the source code?
You are using Nintendo's SDK, right? You might have a bit of trouble distributing that, especially here on GBATemp.

Also, would you mind, de-centering the text for the output? It honestly shouldn't take that much modification to re-locate the text to be left-aligned, and I just want to be sure that this is, indeed, code that you are running and not some video. Preferably, screenshot it with a camera.
 
D

Deleted User

Guest
bot_0004.png


That's a direct screenshot taken with Luma3DS.

I don't know how to prove it other than to post the .cia file which contains the ROM.

Even then people would call fake just because all the goddamn planets in the fucking solar system aren't perfectly aligned at that precise moment in time.

Do you want me to post the source code?
Very well, then. Yes, that would be nice.
Here's another idea: post the .cia, but first, remove the ROM. People could replace the ROM relatively easily.
 
D

Deleted User

Guest
Very well, then. Yes, that would be nice.
Here's another idea: post the .cia, but first, remove the ROM. People could replace the ROM relatively easily.
If he is, indeed, using the SDK, then he can't post the .cia nor the source here on GBATemp, as that would be linking to copyrighted content.
 
D

Deleted User

Guest
If he is, indeed, using the SDK, then he can't post the .cia nor the source here on GBATemp, as that would be linking to copyrighted content.
D'oh, i forgot that detail at that moment.
Well then, fuck. Either it's real or it isn't
 

Thirty3Three

Musician Member
Banned
Joined
Mar 22, 2013
Messages
3,956
Trophies
0
Location
Wherever you want me, baby.
XP
2,605
Country
United States
Sure but it is definitely posible, but it is not worth the amount of work it would need
and even i wouldn't play N64 on 3ds lol
Even with all the work in the world, it probably wouldn't run well on even the N3DS.

--------------------- MERGED ---------------------------

Just a question. What the actual fuck is with the "I KNEW IT! FAKE!" before they even give any reason to show that it could be fake? Or even the SMALLEST COINCIDENCE, and then someone on this fucking forum yells, "scammo" or some shit like that.


The pessimism here is unreal. Fuck you guys who are like that. Jesus Christ it's SO annoying.
 

SimonMKWii

Professional Idiot
OP
Member
Joined
Nov 18, 2017
Messages
666
Trophies
0
Location
Melbourne, Victoria
XP
2,760
Country
Australia
Here's the core code:

Controller Input Handler (Excludes Analog Stick):

Code:
u32
GetDigitalSwitchesPlatform()
{
    u32 Value;
    Value = 0xFFFFFFFF;

    u32 KeysHeld = hidKeysHeld() | hidKeysDown();
    if (KeysHeld & KEY_A)
    {
        Value &= ~JOY_BUTTON_A;
    }

    if (KeysHeld & KEY_B)
    {
        Value &= ~JOY_BUTTON_B;
    }

    if (KeysHeld & KEY_START)
    {
        Value &= ~JOY_BUTTON_START;
    }

    if (KeysHeld & KEY_DRIGHT)
    {
        Value &= ~JOY_BUTTON_RIGHT;
    }

    if (KeysHeld & KEY_DLEFT)
    {
        Value &= ~JOY_BUTTON_LEFT;
    }

    if (KeysHeld & KEY_DUP)
    {
        Value &= ~JOY_BUTTON_UP;
    }

    if (KeysHeld & KEY_DDOWN)
    {
        Value &= ~JOY_BUTTON_DOWN;
    }

    if (KeysHeld & KEY_R)
    {
        Value &= ~JOY_BUTTON_R1;
    }

    if (KeysHeld & KEY_L)
    {
        Value &= ~JOY_BUTTON_L1;
    }

    if (KeysHeld & KEY_ZR)
    {
        Value &= ~JOY_BUTTON_Z;
    }
    return Value;

    if (KeysHeld & KEY_ZL & DUP)
    {
        Value &= ~JOY_BUTTON_C_UP;
    }

    if (KeysHeld & KEY_ZL & DDOWN)
    {
        Value &= ~JOY_BUTTON_C_DOWN;
    }

    if (KeysHeld & KEY_ZL & DLEFT)
    {
        Value &= ~JOY_BUTTON_C_LEFT;
    }

    if (KeysHeld & KEY_ZL & DRIGHT)
    {
        Value &= ~JOY_BUTTON_C_RIGHT;
    }

}

RAM Map:

Code:
void
ResetCpu(MIPS_R3000 *Cpu)
{
    Cpu->pc = RESET_VECTOR;
}

static u32 InterruptMask;

int main(int argc, char **argv)
{
    InitPlatform(argc, argv);

    MIPS_R3000 Cpu;
    SignalProcessor *SP = new SignalProcessor();

    FILE *f = fopen("n64_ipl.bin");
    if (!f) {
        printf("IPL file is not present, please recompile.");
        return -1;
    }
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    u8 *BiosBuffer = (u8 *)linearAlloc(0x800);
    fread(BiosBuffer, fsize, 1, f);
    fclose(f);
    memset(BiosBuffer + 0x7C0, 0, 64);

    u8 *CartBuffer = (u8 *)linearAlloc(0x01000000);
    z64 Z64Cart;
    if (!Z64Open(&Z64Cart, Z64_FLAG_MIDDLE_ENDIAN, "Mario64.v64")) {
        printf("Mario64.v64 is not present, please recompile.");
        return -1;
    }
    Z64Read(&Z64Cart, CartBuffer, Z64GetCartSize(&Z64Cart));
    Z64Close(&Z64Cart);

    MapMemoryRegion(&Cpu, (mmm) {linearAlloc(0x400000), 0x00000000, 0x400000, MEM_REGION_RW});
    MapMemoryRegion(&Cpu, (mmm) {BiosBuffer, 0x1FC00000, 0x07C0, MEM_REGION_READ});
    MapMemoryRegion(&Cpu, (mmm) {BiosBuffer + 0x7C0, 0x1FC007C0, 64, MEM_REGION_RW});
    MapMemoryRegion(&Cpu, (mmm) {linearAlloc(sizeof(VideoInterface)), 0x04400000, sizeof(VideoInterface), MEM_REGION_RW});
    MapMemoryRegion(&Cpu, (mmm) {CartBuffer, 0x10000000, 0x01000000, MEM_REGION_READ});
    MapMemoryRegion(&Cpu, (mmm) {SP, 0xA4040000, sizeof(SignalProcessor), MEM_REGION_RW});
    MapMemoryRegion(&Cpu, (mmm) {linearAlloc(0x1000), 0xA4000000, 0x1000, MEM_REGION_RW});
    MapMemoryRegion(&Cpu, (mmm) {linearAlloc(0x1000), 0xA4001000, 0x1000, MEM_REGION_RW});
    MapMemoryRegion(&Cpu, (mmm) {linearAlloc(sizeof(PeripheralInterface)), 0x4600000, sizeof(PeripheralInterface), MEM_REGION_RW});

    WriteMemWordRaw(&Cpu, 0x10000000 + 0x40 + 0x062C, 0);
    WriteMemWordRaw(&Cpu, 0x10000000 + 0x40 + 0x0638, 0);

    PIFConfig PIF = {(u8 *)MapVirtualAddress(&Cpu, 0x1FC007C0, MEM_REGION_RW), nullptr, 0};
    PIFStartThread(&PIF);
    VIStartThread(&Cpu, (VideoInterface *)MapVirtualAddress(&Cpu, 0x04400000, MEM_REGION_RW));
    PIStartThread(&Cpu, (PeripheralInterface *)MapVirtualAddress(&Cpu, 0x04600000, MEM_REGION_RW));

    ResetCpu(&Cpu);

    bool Step = false;
    int CyclesToRun = 10000;
    bool AutoStep = false;
    u32 IRQ0Steps = 0;

    if (PlatformHasDebugger())
    {
        CyclesToRun = 1;
        AutoStep = false;
        PlatformAttachDebugger(&Cpu);
    }

    while (MainLoopPlatform())
    {

        if (Step || AutoStep)
        {
            Step = false;
            StepCpu(&Cpu, CyclesToRun);
            IRQ0Steps += CyclesToRun;
            if (IRQ0Steps >= 50000)
            {
                C0GenerateException(&Cpu, C0_CAUSE_INT, Cpu.pc - 4);
                IRQ0Steps = 0;
                InterruptMask |= 1;
            }
        }

        SwapBuffersPlatform();
    }

// Redacted SDK code

    PICloseThread();
    VICloseThread();
    PIFCloseThread();
    ExitPlatform();

    return 0;
}

PI RAM:

Code:
static MIPS_R3000 *Cpu;
static PeripheralInterface *PI;
static bool KeepThreadActive;
static void *ThreadHandle;

static void
PIUpdateRoutine()
{
    while (KeepThreadActive)
    {
        if (PI->Status & __builtin_bswap32(0x2)) PI->Status &= ~ __builtin_bswap32(0x2);
        if (PI->Status & __builtin_bswap32(0x1)) PI->Status &= ~ __builtin_bswap32(0x1);
        u32 CartToRAMLen = __builtin_bswap32(PI->WriteLen);
        if (CartToRAMLen)
        {
            PI->Status |= __builtin_bswap32(PI_STATUS_DMA_BUSY);
            u32 RAMAddr = __builtin_bswap32(PI->DRAMAddr) & 0x00FFFFFF;
            u32 CartAddr = __builtin_bswap32(PI->CartAddr) & 0x1FFFFFFF;
            printf("\x1b[33mDMA from Cart %08lX to DRAM %08lX of size %d\x1b[0m\n", CartAddr, RAMAddr, CartToRAMLen);
            for (u32 i = 0; i < CartToRAMLen; i += 4)
            {
                u32 Value = ReadMemWordRaw(Cpu, CartAddr + i);
                WriteMemWordRaw(Cpu, RAMAddr + i, Value);
            }
            PI->WriteLen = 0;
            PI->Status &= ~__builtin_bswap32(PI_STATUS_DMA_BUSY);
        }

        PlatformSleepThread(PLATFORM_SLEEP_SECONDS(1));
    }
}


void
PIStartThread(MIPS_R3000 *C, PeripheralInterface *P)
{
    PI = P;
    Cpu = C;
    KeepThreadActive = true;
    memset(P, 0, sizeof(PeripheralInterface));
    ThreadHandle = PlatformCreateThread(PIUpdateRoutine);
}

void
PICloseThread()
{
    KeepThreadActive = false;
    PlatformJoinThread(ThreadHandle);
}

PIF RAM:

Code:
static PIFConfig *Config;
static bool KeepThreadActive;
static void *ThreadHandle;
static u8 ReqStatus[3] = {0x05, 0x00, 0xFF};

static void
PIFUpdateRoutine()
{
    while (KeepThreadActive)
    {
        u8 *RAM = Config->RAM;
        u8 Status = RAM[0x3F];
        if (Status & 0x20)
        {
            RAM[0x3F] |= PIF_BUSY_BIT;
        }
        if (Status == 1)
        {
            RAM[0x3F] = PIF_BUSY_BIT;
            Config->ChannelCounter = 0;
            for (u32 i = 0; i < 0x3F; ++i)
            {
                s8 TxBytes = RAM[i];
                if ((u8)TxBytes == 0xFE) break;
                if (TxBytes < 0) continue;
                if (TxBytes == 0)
                {
                    ++Config->ChannelCounter;
                    continue;
                }
                u8 RxBytes = RAM[++i] & 0x3F;
                u32 RBIndex = i;
                u8 Command = RAM[++i];
                switch (Command)
                {
                    case PIF_COMMAND_REQUEST_STATUS:
                    {
                        if (RxBytes > 3)
                        {
                            RxBytes = 3;
                            RAM[RBIndex] |= PIF_ERROR_BAD_TRANSFER_SIZE;
                        }
                        if (TxBytes > 1)
                        {
                            RAM[RBIndex] |= PIF_ERROR_BAD_TRANSFER_SIZE;
                        }
                        for (u32 e = 0; e < RxBytes; ++e)
                        {
                            RAM[++i] = ((e == 2) ? 0x02 : ReqStatus[e]);
                        }
                    }

                    default:
                    {
                        printf("Unkown PIF command %02X\n", Command);
                        i += RxBytes;
                    }
                }
                ++Config->ChannelCounter;

            }

            RAM[0x3F] = 0;
        }

        PlatformSleepThread(PLATFORM_SLEEP_SECONDS(1));
    }
}

void
PIFStartThread(PIFConfig *C)
{
    Config = C;
    KeepThreadActive = true;
    ThreadHandle = PlatformCreateThread(PIFUpdateRoutine);
}

void
PIFCloseThread()
{
    KeepThreadActive = false;
    PlatformJoinThread(ThreadHandle);
}

Need I post more?
 
Last edited by SimonMKWii,

Jayro

MediCat USB Dev
Developer
Joined
Jul 23, 2012
Messages
12,973
Trophies
4
Location
WA State
Website
ko-fi.com
XP
17,003
Country
United States
I remember the PSP had an N64 emulator on it, but it too ran like shit, with only 64MB of RAM to work with, and a 333MHz CPU. I think the fact that they shared a MIPS architecture was about the only reason it even worked at all.
 
  • Like
Reactions: Alkéryn

SimonMKWii

Professional Idiot
OP
Member
Joined
Nov 18, 2017
Messages
666
Trophies
0
Location
Melbourne, Victoria
XP
2,760
Country
Australia
More source code:

Full MIPS CPU Interpreter Code:

Code:
inline u64
ReadMemDWord(MIPS_R3000 *Cpu, u64 Address)
{
    u64 Base = Address & 0x1FFFFFFF;
    u8 *VirtualAddress = (u8 *)MapVirtualAddress(Cpu, Address, MEM_REGION_READ);
    u32 Swap = Cpu->CP0.sr & C0_STATUS_RE;
    u64 Value = DEFAULT_UNMAPPED_ADDR_VALUE;
    if (!VirtualAddress)
    {
        for (u32 i = 0; i < Cpu->NumMMR; ++i)
        {
            mmr *MMR = &Cpu->MemMappedRegisters[i];
            if (Base == MMR->Address)
            {
                Value = MMR->RegisterReadFunc(MMR->Object, Address);
                return (Swap ? Value: __builtin_bswap64(Value));
            }
        }
        printf("%08lX: Read dword from unmapped address: 0x%08lX\n", (u32)Cpu->pc - 8, (u32)Address);
        return Value;
    }
    Value = *((u64 *)VirtualAddress);
    return (Swap ? Value: __builtin_bswap64(Value));
}

inline u32
ReadMemWord(MIPS_R3000 *Cpu, u64 Address)
{
    u32 Base = Address & 0x1FFFFFFF;
    u8 *VirtualAddress = (u8 *)MapVirtualAddress(Cpu, Address, MEM_REGION_READ);
    u32 Swap = Cpu->CP0.sr & C0_STATUS_RE;
    u32 Value = DEFAULT_UNMAPPED_ADDR_VALUE;
    if (!VirtualAddress)
    {
        for (u32 i = 0; i < Cpu->NumMMR; ++i)
        {
            mmr *MMR = &Cpu->MemMappedRegisters[i];
            if (Base == MMR->Address)
            {
                Value = MMR->RegisterReadFunc(MMR->Object, Address);
                return (Swap ? Value: __builtin_bswap32(Value));
            }
        }

        printf("%08lX: Read word from unmapped address: 0x%08lX\n", (u32)Cpu->pc - 8, (u32)Address);
        return Value;
    }
    Value = *((u32 *)VirtualAddress);
    return (Swap ? Value: __builtin_bswap32(Value));
}

static void
WriteMemByte(MIPS_R3000 *Cpu, u64 Address, u8 Value)
{
    u32 Base = Address & 0x1FFFFFFF;
    u8 *VirtualAddress = (u8 *)MapVirtualAddress(Cpu, Address, MEM_REGION_WRITE);
    if (!VirtualAddress)
    {
        for (u32 i = 0; i < Cpu->NumMMR; ++i)
        {
            mmr *MMR = &Cpu->MemMappedRegisters[i];
            if (Base == MMR->Address)
            {
                
                MMR->RegisterWriteFunc(MMR->Object, Value);
                return;
            }
        }

        printf("%08lX: Write byte 0x%02X to unmapped address: 0x%08lX\n", (u32)Cpu->pc - 8, Value, (u32)Address);
        return;
    }
    *((u8 *)VirtualAddress) = Value;
}

static void
WriteMemDWord(MIPS_R3000 *Cpu, u64 Address, u64 Value)
{
    u32 Base = Address & 0x1FFFFFFF;
    u8 *VirtualAddress = (u8 *)MapVirtualAddress(Cpu, Address, MEM_REGION_WRITE);
    u32 Swap = Cpu->CP0.sr & C0_STATUS_RE;
    if (!VirtualAddress)
    {
        for (u32 i = 0; i < Cpu->NumMMR; ++i)
        {
            mmr *MMR = &Cpu->MemMappedRegisters[i];
            if (Base == MMR->Address)
            {
                MMR->RegisterWriteFunc(MMR->Object, (Swap ? Value: __builtin_bswap64(Value)));
                return;
            }
        }

        printf("%08lX: Write dword 0x%016llX to unmapped address: 0x%08lX\n", (u32)Cpu->pc - 8, Value, (u32)Address);
        return;
    }
    *((u64 *)((u8 *)VirtualAddress)) = (Swap ? Value: __builtin_bswap64(Value));
}

static void
WriteMemWord(MIPS_R3000 *Cpu, u64 Address, u32 Value)
{
    u32 Base = Address & 0x1FFFFFFF;
    u8 *VirtualAddress = (u8 *)MapVirtualAddress(Cpu, Address, MEM_REGION_WRITE);
    u32 Swap = Cpu->CP0.sr & C0_STATUS_RE;
    if (!VirtualAddress)
    {
        for (u32 i = 0; i < Cpu->NumMMR; ++i)
        {
            mmr *MMR = &Cpu->MemMappedRegisters[i];
            if (Base == MMR->Address)
            {
                MMR->RegisterWriteFunc(MMR->Object, (Swap ? Value: __builtin_bswap32(Value)));
                return;
            }
        }

        printf("%08lX: Write word 0x%08lX to unmapped address: 0x%08lX\n", (u32)Cpu->pc - 8, Value, (u32)Address);
        return;
    }
    *((u32 *)((u8 *)VirtualAddress)) = (Swap ? Value: __builtin_bswap32(Value));
}

static void
WriteMemHalfWord(MIPS_R3000 *Cpu, u64 Address, u16 Value)
{
    u32 Base = Address & 0x1FFFFFFF;
    u8 *VirtualAddress = (u8 *)MapVirtualAddress(Cpu, Address, MEM_REGION_WRITE);
    u32 Swap = Cpu->CP0.sr & C0_STATUS_RE;
    if (!VirtualAddress)
    {
        for (u32 i = 0; i < Cpu->NumMMR; ++i)
        {
            mmr *MMR = &Cpu->MemMappedRegisters[i];
            if (Base == MMR->Address)
            {
                MMR->RegisterWriteFunc(MMR->Object, (Swap ? Value: __builtin_bswap16(Value)));
                return;
            }
        }

        printf("%08lX: Write half-word 0x%04lX to unmapped address: 0x%08lX\n", (u32)Cpu->pc - 8, Value, (u32)Address);
        return;
    }
    *((u16 *)((u8 *)VirtualAddress)) = (Swap ? Value: __builtin_bswap16(Value));
}

static void
C0ExecuteOperation(Coprocessor *Cp, u32 FunctionCode);

MIPS_R3000::
MIPS_R3000()
{
    CP0.ExecuteOperation = C0ExecuteOperation;
    NumMMR = 0;
    NumMMM = 0;
}

inline void
C0ExceptionPushSRBits(Coprocessor *CP0)
{
    u64 SR = CP0->sr;
    u32 IEp = (SR >> 2) & 1;
    u32 KUp = (SR >> 3) & 1;
    u32 IEc = (SR) & 1;
    u32 KUc = (SR >> 1) & 1;
    SR ^= (-IEp ^ SR) & (1 << 4);
    SR ^= (-KUp ^ SR) & (1 << 5);
    SR ^= (-IEc ^ SR) & (1 << 2);
    SR ^= (-KUc ^ SR) & (1 << 3);
    CP0->sr = SR;
}

inline void
C0ExceptionPopSRBits(Coprocessor *CP0)
{
    u64 SR = CP0->sr;
    u32 IEp = (SR >> 2) & 1;
    u32 KUp = (SR >> 3) & 1;
    u32 IEo = (SR >> 4) & 1;
    u32 KUo = (SR >> 5) & 1;
    SR ^= (-IEp ^ SR) & (1 << 0);
    SR ^= (-KUp ^ SR) & (1 << 1);
    SR ^= (-IEo ^ SR) & (1 << 2);
    SR ^= (-KUo ^ SR) & (1 << 3);
    CP0->sr = SR;
}

void
C0GenerateException(MIPS_R3000 *Cpu, u8 Cause, u64 EPC)
{
    if (Cpu->CP0.sr & C0_STATUS_IEc)
    {
        Cpu->CP0.cause = (Cause << 2) & C0_CAUSE_MASK;
        Cpu->CP0.epc = EPC;
        C0ExceptionPushSRBits(&Cpu->CP0);
        Cpu->CP0.sr &= ~C0_STATUS_KUc;
        Cpu->CP0.sr &= ~C0_STATUS_IEc;
        Cpu->pc = GNRAL_VECTOR;
    }
}

static void
C0GenerateSoftwareException(MIPS_R3000 *Cpu, u8 Cause, u64 EPC)
{
    Cpu->CP0.cause = (Cause << 2) & C0_CAUSE_MASK;
    Cpu->CP0.epc = EPC;
    C0ExceptionPushSRBits(&Cpu->CP0);
    Cpu->CP0.sr &= ~C0_STATUS_KUc;
    Cpu->CP0.sr &= ~C0_STATUS_IEc;
    Cpu->pc = GNRAL_VECTOR;
}

static void
C0ReturnFromException(Coprocessor *Cp)
{
    C0ExceptionPopSRBits(Cp);
}

static void
C0ExecuteOperation(Coprocessor *Cp, u32 FunctionCode)
{
    if (FunctionCode == 0x10)
    {
        C0ReturnFromException(Cp);
    }
}

static void
ReservedInstructionException(MIPS_R3000 *Cpu, opcode *Op, u32 Data)
{
    C0GenerateException(Cpu, C0_CAUSE_RI, Op->CurrentAddress);
}

static void
SysCall(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    C0GenerateSoftwareException(Cpu, C0_CAUSE_SYSCALL, OpCode->CurrentAddress);
}

static void
Break(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    C0GenerateException(Cpu, C0_CAUSE_BKPT, OpCode->CurrentAddress);
}

typedef void (*jt_func)(MIPS_R3000 *, opcode *, u32 Data);

static void
AddU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rs] + Cpu->GPR[rt];
    }
}

static void
AddIU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 Immediate = SignExtend16((Data & IMM16_MASK) >> 0);
        Cpu->GPR[rt] = (u32)((Cpu->GPR[rs] & 0xFFFFFFFF) + Immediate);
    }
}

static void
DAddIU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK) >> 0);
        Cpu->GPR[rt] = Cpu->GPR[rs] + Immediate;
    }
}

static void
SubU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rs] - Cpu->GPR[rt];
    }
}

static void
Add(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rs] + Cpu->GPR[rt];
    }
}

static void
AddI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 Immediate = SignExtend16((Data & IMM16_MASK) >> 0);
        Cpu->GPR[rt] = (u32)((Cpu->GPR[rs] & 0xFFFFFFFF) + Immediate);
    }
}

static void
DAddI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK) >> 0);
        Cpu->GPR[rt] = Cpu->GPR[rs] + Immediate;
    }
}

static void
Sub(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rs] - Cpu->GPR[rt];
    }
}

static void
MFHI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;
    Cpu->GPR[rd] = Cpu->hi;
}

static void
MFLO(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;
    Cpu->GPR[rd] = Cpu->lo;
}

static void
MTHI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    Cpu->hi = rs;
}

static void
MTLO(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    Cpu->lo = rs;
}

static void
Mult(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    s64 Result = (s64)((Cpu->GPR[rs] & 0xFFFFFFFF) * (Cpu->GPR[rt] & 0xFFFFFFFF));
    Cpu->hi = SignExtend32To64((Result >> 32) & 0xFFFFFFFF);
    Cpu->lo = SignExtend32To64(Result & 0xFFFFFFFF);
}

static void
MultU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    u64 Result = (u64)((Cpu->GPR[rs] & 0xFFFFFFFF) * (Cpu->GPR[rt] & 0xFFFFFFFF));
    Cpu->hi = SignExtend32To64((Result >> 32) & 0xFFFFFFFF);
    Cpu->lo = SignExtend32To64(Result & 0xFFFFFFFF);
}

static void
Div(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    s64 Left = Cpu->GPR[rs];
    s64 Right = Cpu->GPR[rt];
    if (!Right)
    {
        Cpu->hi = Left;
        if (Left >= 0)
        {
            Cpu->lo = -1;
        }
        else
        {
            Cpu->lo = 1;
        }
        return;
    }
    if (Right == -1 && (u64)Left == 0x8000000000000000)
    {
        Cpu->hi = 0;
        Cpu->lo = 0x8000000000000000;
        return;
    }
    Cpu->lo = Left / Right;
    Cpu->hi = Left % Right;
}

static void
DivU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    u64 Left = Cpu->GPR[rs];
    u64 Right = Cpu->GPR[rt];
    if (!Right)
    {
        Cpu->hi = Left;
        Cpu->lo = 0xFFFFFFFFFFFFFFFF;
        return;
    }
    Cpu->lo = Left / Right;
    Cpu->hi = Left % Right;
}

static void
SW(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_WORD | MEM_ACCESS_WRITE;
}

static void
SD(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_DWORD | MEM_ACCESS_WRITE;
}

static void
SDL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_DWORD | MEM_ACCESS_WRITE | MEM_ACCESS_HIGH;
}

static void
SDR(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_DWORD | MEM_ACCESS_WRITE | MEM_ACCESS_LOW;
}

static void
SWL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_WORD | MEM_ACCESS_WRITE | MEM_ACCESS_HIGH;
}

static void
SWR(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_WORD | MEM_ACCESS_WRITE | MEM_ACCESS_LOW;
}

static void
SH(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_HALF | MEM_ACCESS_WRITE;
}

static void
SB(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
    OpCode->MemAccessValue = Cpu->GPR[rt];
    OpCode->MemAccessMode = MEM_ACCESS_BYTE | MEM_ACCESS_WRITE;
}

static void
LUI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 Immediate = (Data & IMM16_MASK) >> 0;
        Cpu->GPR[rt] = Immediate << 16;
    }
}

static void
LW(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_WORD | MEM_ACCESS_SIGNED;
    }
}

static void
LWU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_WORD;
    }
}

static void
LWL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_WORD | MEM_ACCESS_SIGNED | MEM_ACCESS_HIGH;
    }
}

static void
LWR(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_WORD | MEM_ACCESS_SIGNED | MEM_ACCESS_LOW;
    }
}

static void
LD(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_DWORD;
    }
}

static void
LDL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_DWORD | MEM_ACCESS_HIGH;
    }
}

static void
LDR(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_DWORD | MEM_ACCESS_LOW;
    }
}

static void
LBU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_BYTE;
    }
}

static void
LHU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_HALF;
    }
}

static void
LB(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_BYTE | MEM_ACCESS_SIGNED;
    }
}

static void
LH(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;
    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        OpCode->MemAccessAddress = Cpu->GPR[rs] + Immediate;
        OpCode->MemAccessValue = rt;
        OpCode->MemAccessMode = MEM_ACCESS_READ | MEM_ACCESS_HALF | MEM_ACCESS_SIGNED;
    }
}


static void
J(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u64 Immediate = (Data & IMM26_MASK);
    Cpu->pc = (Cpu->pc & 0xF0000000) + (Immediate * 4);
}

static void
JAL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 Immediate = (Data & IMM26_MASK);
    Cpu->ra = OpCode->CurrentAddress + 8;
    Cpu->pc = (Cpu->pc & 0xF0000000) + (Immediate * 4);
}

static void
JR(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    Cpu->pc = Cpu->GPR[rs];
}

static void
JALR(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rd = (Data & REG_RD_MASK) >> 11;
    Cpu->pc = Cpu->GPR[rs];
    if (rd) Cpu->GPR[rd] = OpCode->CurrentAddress + 8;
}

static void
BranchZero(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    u64 Immediate = SignExtend16To64((Data & IMM16_MASK));

    u64 Address = OpCode->CurrentAddress + 4 + Immediate * 4;
    s64 Check = Cpu->GPR[rs];

    if (rt & 0b00001)
    {
        if (Check >= 0)
        {
            if (rt & 0b10000)
            {
                Cpu->ra = OpCode->CurrentAddress + 8;
            }
            Cpu->pc = Address;
        }
    }
    else
    {
        if (Check < 0)
        {
            if (rt & 0b10000)
            {
                Cpu->ra = OpCode->CurrentAddress + 8;
            }
            Cpu->pc = Address;
        }
    }
}

static void
BEQ(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;
    
    if (Cpu->GPR[rs] == Cpu->GPR[rt])
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    }
}

static void
BEQL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (Cpu->GPR[rs] == Cpu->GPR[rt])
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    } else {
        Cpu->SkipExecute = true;
    }
}

static void
BNE(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (Cpu->GPR[rs] != Cpu->GPR[rt])
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    }
}

static void
BNEL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (Cpu->GPR[rs] != Cpu->GPR[rt])
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    } else {
        Cpu->SkipExecute = true;
    }
}

static void
BLEZ(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;

    if (Cpu->GPR[rs] <= 0)
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    }
}

static void
BLEZL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;

    if (Cpu->GPR[rs] <= 0)
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    } else {
        Cpu->SkipExecute = true;
    }
}

static void
BGTZ(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;

    if (Cpu->GPR[rs] > 0)
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    }
}

static void
BGTZL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;

    if (Cpu->GPR[rs] > 0)
    {
        u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
        Cpu->pc = OpCode->CurrentAddress + 4 + Immediate * 4;
    } else {
        Cpu->SkipExecute = true;
    }
}

static void
AndI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 Immediate = Data & IMM16_MASK;
        Cpu->GPR[rt] = Cpu->GPR[rs] & Immediate;
    }
}

static void
OrI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 Immediate = Data & IMM16_MASK;
        Cpu->GPR[rt] = Cpu->GPR[rs] | Immediate;
    }
}

static void
And(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rs] & Cpu->GPR[rt];
    }
}

static void
Or(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rs] | Cpu->GPR[rt];
    }
}

static void
XOr(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rs] ^ Cpu->GPR[rt];
    }
}
static void
NOr(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = 0xFFFFFFFF ^ (Cpu->GPR[rs] | Cpu->GPR[rt]);
    }
}

static void
XOrI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 Immediate = Data & IMM16_MASK;
        Cpu->GPR[rt] = Cpu->GPR[rs] ^ Immediate;
    }
}

static void
SLLV(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rt] << (Cpu->GPR[rs] & 0x1F);
    }
}

static void
SRLV(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rt] >> (Cpu->GPR[rs] & 0x1F);
    }
}

static void
SRAV(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = ((s32)Cpu->GPR[rt]) >> (Cpu->GPR[rs] & 0x1F);
    }
}

static void
SLL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 Immediate = (Data & IMM5_MASK) >> 6;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rt] << Immediate;
    }
}

static void
SRL(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 Immediate = (Data & IMM5_MASK) >> 6;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = Cpu->GPR[rt] >> Immediate;
    }
}

static void
SRA(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 Immediate = (Data & IMM5_MASK) >> 6;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = ((s32)Cpu->GPR[rt]) >> Immediate;
    }
}

static void
SLT(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = ( ((s32)Cpu->GPR[rs] < (s32)Cpu->GPR[rt]) ? 1 : 0);
    }
}

static void
SLTU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rd = (Data & REG_RD_MASK) >> 11;

    if (rd)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 rt = (Data & REG_RT_MASK) >> 16;
        Cpu->GPR[rd] = ( (Cpu->GPR[rs] < Cpu->GPR[rt]) ? 1 : 0);
    }
}

static void
SLTI(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 Immediate = SignExtend16((Data & IMM16_MASK) >> 0);
        Cpu->GPR[rt] = ( ((s32)Cpu->GPR[rs] < (s32)Immediate) ? 1 : 0);
    }
}

static void
SLTIU(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rt = (Data & REG_RT_MASK) >> 16;

    if (rt)
    {
        u32 rs = (Data & REG_RS_MASK) >> 21;
        u32 Immediate = (Data & IMM16_MASK) >> 0;
        Cpu->GPR[rt] = ( (Cpu->GPR[rs] < Immediate) ? 1 : 0);
    }
}

static void
COP0(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;
    u32 rd = (Data & REG_RD_MASK) >> 11;

    Coprocessor *CP0 = &Cpu->CP0;

    if (rs < 0b001000 && rs > 0b00010)
    {
        CP0->registers[rd + (rs & 0b00010 ? 32 : 0)] = Cpu->GPR[rt];
    }
    else if (rs < 0b10000)
    {
        if (rs < 0b00100)
        {
            if (rt) Cpu->GPR[rt] = CP0->registers[rd + (rs & 0b00010 ? 32 : 0)];
        }
        else
        {
            u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
            if (rt)
            {
                if (CP0->sr & C0_STATUS_CU0)
                {
                    Cpu->pc = OpCode->CurrentAddress + Immediate;
                }
            }
            else
            {
                if ((CP0->sr & C0_STATUS_CU0) == 0)
                {
                    Cpu->pc = OpCode->CurrentAddress + Immediate;
                }
            }
        }
    }
    else
    {
        CP0->ExecuteOperation(CP0, (Data & IMM25_MASK));
    }
}

static void
COP2(MIPS_R3000 *Cpu, opcode *OpCode, u32 Data)
{
    u32 rs = (Data & REG_RS_MASK) >> 21;
    u32 rt = (Data & REG_RT_MASK) >> 16;
    u32 rd = (Data & REG_RD_MASK) >> 11;

    Coprocessor *CP2 = Cpu->CP2;
    if (rs < 0b001000 && rs > 0b00010)
    {
        CP2->registers[rd + (rs & 0b00010 ? 32 : 0)] = Cpu->GPR[rt];
    }
    else if (rs < 0b10000)
    {
        if (rs < 0b00100)
        {
            if (rt) Cpu->GPR[rt] = CP2->registers[rd + (rs & 0b00010 ? 32 : 0)];
        }
        else
        {
            u64 Immediate = SignExtend16To64((Data & IMM16_MASK));
            if (rt)
            {
                if (Cpu->CP0.sr & C0_STATUS_CU2)
                {
                    Cpu->pc = OpCode->CurrentAddress + Immediate;
                }
            }
            else
            {
                if ((Cpu->CP0.sr & C0_STATUS_CU2) == 0)
                {
                    Cpu->pc = OpCode->CurrentAddress + Immediate;
                }
            }
        }
    }
    else
    {
        CP2->ExecuteOperation(CP2, (Data & IMM25_MASK));
    }
}


inline u32
InstructionFetch(MIPS_R3000 *Cpu)
{
    u32 Result = ReadMemWord(Cpu, Cpu->pc);
    Cpu->pc += 4;
    return Result;
}

inline void
MemoryAccess(MIPS_R3000 *Cpu, opcode *OpCode)
{
    u64 Address = OpCode->MemAccessAddress;
    u64 Value = OpCode->MemAccessValue;
    u32 MemAccessMode = OpCode->MemAccessMode;

    if (MemAccessMode & MEM_ACCESS_WRITE)
    {
        if (MemAccessMode & MEM_ACCESS_BYTE)
        {
            WriteMemByte(Cpu, Address, (u8)Value);
        }

        else if (MemAccessMode & MEM_ACCESS_HALF)
        {
            WriteMemHalfWord(Cpu, Address, (u16)Value);
        }

        else if (MemAccessMode & MEM_ACCESS_WORD)
        {
            if (MemAccessMode & MEM_ACCESS_HIGH)
            {
                Value = (Value & 0xFFFF0000) | (ReadMemWordRaw(Cpu, Address) & 0x0000FFFF);
            }
            else if (MemAccessMode & MEM_ACCESS_LOW)
            {
                Value = (Value & 0x0000FFFF) | (ReadMemWordRaw(Cpu, Address) & 0xFFFF0000);
            }
            WriteMemWord(Cpu, Address, (u32)Value);
        }

        else if (MemAccessMode & MEM_ACCESS_DWORD)
        {
            if (MemAccessMode & MEM_ACCESS_HIGH)
            {
                Value = (Value & 0xFFFFFFFF00000000) | (ReadMemDWordRaw(Cpu, Address) & 0x00000000FFFFFFFF);
            }
            else if (MemAccessMode & MEM_ACCESS_LOW)
            {
                Value = (Value & 0x00000000FFFFFFFF) | (ReadMemDWordRaw(Cpu, Address) & 0xFFFFFFFF00000000);
            }
            WriteMemDWord(Cpu, Address, Value);
        }
    }
    else if (MemAccessMode & MEM_ACCESS_READ)
    {
        if (Value)
        {
            u32 Register = (u32)Value;
            u32 Signed = MemAccessMode & MEM_ACCESS_SIGNED;
            Value = ReadMemDWord(Cpu, Address);
            if (MemAccessMode & MEM_ACCESS_BYTE)
            {
                Value = (Value >> 56) & 0xFF;
                if (Signed)
                {
                    Value = SignExtend8To64(Value);
                }
            }

            else if (MemAccessMode & MEM_ACCESS_HALF)
            {
                Value = (Value >> 48) & 0xFFFF;
                if (Signed)
                {
                    Value = SignExtend16To64(Value);
                }
            }

            else if (MemAccessMode & MEM_ACCESS_WORD)
            {
                Value = (Value >> 32) & 0xFFFFFFFF;
                if (Signed)
                {
                    Value = SignExtend32To64(Value);
                }
                if (MemAccessMode & MEM_ACCESS_HIGH)
                {
                    Value = SignExtend32To64((Cpu->GPR[Register] & 0x0000FFFF) | (Value & 0xFFFF0000));
                }
                else if (MemAccessMode & MEM_ACCESS_LOW)
                {
                    Value = SignExtend32To64((Cpu->GPR[Register] & 0xFFFF0000) | (Value & 0x0000FFFF));
                }
            }

            else if (MemAccessMode & MEM_ACCESS_DWORD)
            {
                if (MemAccessMode & MEM_ACCESS_HIGH)
                {
                    Value = (Cpu->GPR[Register] & 0xFFFFFFFF) | (Value & 0xFFFFFFFF00000000);
                }
                else if (MemAccessMode & MEM_ACCESS_LOW)
                {
                    Value = (Cpu->GPR[Register] & 0xFFFFFFFF00000000) | (Value & 0xFFFFFFFF);
                }
            }
            Cpu->GPR[Register] = Value;
        }
    }
}

void
MapRegister(MIPS_R3000 *Cpu, mmr MMR)
{
    Cpu->MemMappedRegisters[Cpu->NumMMR] = MMR;
    ++Cpu->NumMMR;
}

void
MapMemoryRegion(MIPS_R3000 *Cpu, mmm MMM)
{
    Cpu->MemMappedMemRegions[Cpu->NumMMM] = MMM;
    ++Cpu->NumMMM;
}

void
StepCpu(MIPS_R3000 *Cpu, u32 Steps)
{
    void *FJTPrimary[0x40] =
    {
        &&_ReservedInstructionException,
        &&_BranchZero,
        &&_Jump,
        &&_JumpAL,
        &&_BEQ,
        &&_BNE,
        &&_BLEZ,
        &&_BGTZ,
        &&_AddI,
        &&_AddIU,
        &&_SLTI,
        &&_SLTIU,
        &&_AndI,
        &&_OrI,
        &&_XOrI,
        &&_LUI,
        &&_COP0,
        &&_COP1,
        &&_COP2,
        &&_COP3,
        &&_BEQL,
        &&_BNEL,
        &&_BLEZL,
        &&_BGTZL,
        &&_DAddI,
        &&_DAddIU,
        &&_LDL,
        &&_LDR,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_LB,
        &&_LH,
        &&_LWL,
        &&_LW,
        &&_LBU,
        &&_LHU,
        &&_LWR,
        &&_LWU,
        &&_SB,
        &&_SH,
        &&_SWL,
        &&_SW,
        &&_SDL,
        &&_SDR,
        &&_SWR,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_LD,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_SD,
    };

    void *FJTSecondary[0x40] =
    {
        &&_SLL,
        &&_ReservedInstructionException,
        &&_SRL,
        &&_SRA,
        &&_SLLV,
        &&_ReservedInstructionException,
        &&_SRLV,
        &&_SRAV,
        &&_JumpR,
        &&_JumpALR,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_Syscall,
        &&_Break,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_MFHI,
        &&_MTHI,
        &&_MFLO,
        &&_MTLO,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_Mult,
        &&_MultU,
        &&_Div,
        &&_DivU,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_Add,
        &&_AddU,
        &&_Sub,
        &&_SubU,
        &&_And,
        &&_Or,
        &&_XOr,
        &&_NOr,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_SLT,
        &&_SLTU,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
        &&_ReservedInstructionException,
    };

    opcode *OpCodes = Cpu->OpCodes;
    u32 BS = Cpu->BaseState;
    void *NextJump = Cpu->NextJump;
    u32 NextData = Cpu->NextData;

    if (NextJump) goto *NextJump;

#define NEXT(Instruction) \
    { \
    if (!Steps) goto _ExitThread; \
    opcode *OpCodeMemAccess = &OpCodes[BS % 2]; \
    if (OpCodeMemAccess->MemAccessMode) \
    { \
        MemoryAccess(Cpu, OpCodeMemAccess); \
    } \
    OpCodeMemAccess->CurrentAddress = Cpu->pc; \
    OpCodeMemAccess->MemAccessMode = MEM_ACCESS_NONE; \
    u32 TempData = InstructionFetch(Cpu); \
    if (!Cpu->SkipExecute) { \
        Instruction(Cpu, &OpCodes[(BS + 1) % 2], NextData); \
    } else { \
        Cpu->SkipExecute = 0; \
    } \
    NextData = TempData; \
    if ((NextData & PRIMARY_OP_MASK) >> 26) \
    { \
        NextJump = FJTPrimary[(NextData & PRIMARY_OP_MASK) >> 26]; \
    } \
    else \
    { \
        u32 Select1 = (NextData & SECONDARY_OP_MASK); \
        NextJump = FJTSecondary[Select1]; \
    } \
    ++BS; \
    --Steps; \
    goto *NextJump; \
    }


_ReservedInstructionException:
    NEXT(ReservedInstructionException);
_BranchZero:
    NEXT(BranchZero);
_Jump:
    NEXT(J);
_JumpAL:
    NEXT(JAL);
_BEQ:
    NEXT(BEQ);
_BNE:
    NEXT(BNE);
_BLEZ:
    NEXT(BLEZ);
_BGTZ:
    NEXT(BGTZ);
_AddI:
    NEXT(AddI);
_AddIU:
    NEXT(AddIU);
_SLTI:
    NEXT(SLTI);
_SLTIU:
    NEXT(SLTIU);
_AndI:
    NEXT(AndI);
_OrI:
    NEXT(OrI);
_XOrI:
    NEXT(XOrI);
_LUI:
    NEXT(LUI);
_COP0:
    NEXT(COP0);
_COP1:
    NEXT(COP1);
_COP2:
    NEXT(COP2);
_COP3:
    NEXT(COP3);
_BEQL:
    NEXT(BEQL);
_BNEL:
    NEXT(BNEL);
_BLEZL:
    NEXT(BLEZL);
_BGTZL:
    NEXT(BGTZL);
_DAddI:
    NEXT(DAddI);
_DAddIU:
    NEXT(DAddIU);
_LDL:
    NEXT(LDL);
_LDR:
    NEXT(LDR);
_LB:
    NEXT(LB);
_LH:
    NEXT(LH);
_LWL:
    NEXT(LWL);
_LW:
    NEXT(LW);
_LBU:
    NEXT(LBU);
_LHU:
    NEXT(LHU);
_LWR:
    NEXT(LWR);
_LWU:
    NEXT(LWU);
_SB:
    NEXT(SB);
_SH:
    NEXT(SH);
_SWL:
    NEXT(SWL);
_SW:
    NEXT(SW);
_SDL:
    NEXT(SDL);
_SDR:
    NEXT(SDR);
_SWR:
    NEXT(SWR);
_LD:
    NEXT(LD);
_SD:
    NEXT(SD);

_SLL:
    NEXT(SLL);
_SRL:
    NEXT(SRL);
_SRA:
    NEXT(SRA);
_SLLV:
    NEXT(SLLV);
_SRLV:
    NEXT(SRLV);
_SRAV:
    NEXT(SRAV);
_JumpR:
    NEXT(JR);
_JumpALR:
    NEXT(JALR);
_Syscall:
    NEXT(SysCall);
_Break:
    NEXT(Break);
_MFHI:
    NEXT(MFHI);
_MTHI:
    NEXT(MTHI);
_MFLO:
    NEXT(MFLO);
_MTLO:
    NEXT(MTLO);
_Mult:
    NEXT(Mult);
_MultU:
    NEXT(MultU);
_Div:
    NEXT(Div);
_DivU:
    NEXT(DivU);
_Add:
    NEXT(Add);
_AddU:
    NEXT(AddU);
_Sub:
    NEXT(Sub);
_SubU:
    NEXT(SubU);
_And:
    NEXT(And);
_Or:
    NEXT(Or);
_XOr:
    NEXT(XOr);
_NOr:
    NEXT(NOr);
_SLT:
    NEXT(SLT);
_SLTU:
    NEXT(SLTU);

_ExitThread:
    Cpu->NextJump = NextJump;
    Cpu->NextData = NextData;
    Cpu->BaseState = BS;
}

Analog Control Stick:

Code:
static u32 RXRead;
static u32 JoySequenceState = 0;
const static u32 JoyID = JOY_ID_DIGITAL_PAD;
static u32 DigitalSwitches = 0;

void
JoyTxWrite(void *Object, u32 Value)
{
    if (!JoySequenceState)
    {
        if (Value == 0x01)
        {
            RXRead = 0xFF;
            ++JoySequenceState;
            return;
        }
    }

    if (JoySequenceState == 1)
    {
        if (Value == 0x42)
        {
            RXRead = JoyID & 0xFF;
            ++JoySequenceState;
            return;
        }
    }

    if (JoySequenceState == 2)
    {
        if (Value == 0)
        {
            RXRead = (JoyID >> 8) & 0xFF;
            ++JoySequenceState;
            return;
        }
    }

    if (JoySequenceState == 3)
    {
        DigitalSwitches = GetDigitalSwitchesPlatform();
        RXRead = DigitalSwitches & 0xFF;
        ++JoySequenceState;
        return;
    }

    if (JoySequenceState == 4)
    {
        RXRead = (DigitalSwitches >> 8) & 0xFF;
        JoySequenceState = 0;
        return;
    }
}

u32
JoyRxRead(void *Object, u32 Address)
{
    return RXRead;
}
 
D

Deleted User

Guest
It took around 50 minutes to produce that bottom screen screenshot. In the OP, there are two screenshots that were captured with Luma. Luma takes both the top and bottom screen screenshots when you take a screenshot. It took OP 50 minutes to plug their 3ds SD card into their computer and copy an existing screenshot.

inb4 I deleted all of my screenshots after uploading them:
Then why is the screenshot you uploaded called bot_004.png if you're always deleting your screenshots?

Why don't you just post a video where you don't suspiciously hide the bottom screen?

Also, it's a bit weird, why does the polygon count and ROM size have a comma, but not the VI refresh rate doesn't?
u5IwgW2.png


Here's the core code:

<lots of code>

Code:
// The main loop of the program
    while (MainLoopPlatform())
    {

        if (Step || AutoStep)
        {
            Step = false;
            StepCpu(&Cpu, CyclesToRun);
            IRQ0Steps += CyclesToRun;
            if (IRQ0Steps >= 50000)
            {
                C0GenerateException(&Cpu, C0_CAUSE_INT, Cpu.pc - 4);
                IRQ0Steps = 0;
                InterruptMask |= 1;
            }
        }

        SwapBuffersPlatform();
    }

// For some reason, SDK code only goes here, when the program is closing.

// Redacted SDK code

    PICloseThread();
    VICloseThread();
    PIFCloseThread();
    ExitPlatform();

    return 0;
}
<more code>

All the code you posted appears to be just directly stolen from CTR64. You even forgot the Beerware license. Tsk, tsk.
How did you combine the official Nintendo SDK with the homebrew SDK?
You should also post the Z64Open method, the one you pass the "Mario64.v64" string to. Remember, it can't just be using fopen. There has to be some special code because the rom is embedded into the program. The original CTR64 code just says "Couldn't open" if it can't open the rom file, your code says to recompile, so you're definitely still claiming it's an embedded file.

Edit:
Make a method that uses fopen after appending "romfs:" to the beginning of the string. I dare you.
 
Last edited by ,
Status
Not open for further replies.

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    rvtr @ rvtr: Spam bots again.