// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include <cinttypes>
#include <string>

#include "disasm.h"

#include "Common/Assert.h"
#include "Common/BitSet.h"
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "Common/x64Emitter.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/JitCommon/JitBase.h"

using namespace Gen;

// This generates some fairly heavy trampolines, but it doesn't really hurt.
// Only instructions that access I/O will get these, and there won't be that
// many of them in a typical program/game.
bool Jitx86Base::HandleFault(uintptr_t access_address, SContext* ctx)
{
  // TODO: do we properly handle off-the-end?
  if (access_address >= (uintptr_t)Memory::physical_base &&
      access_address < (uintptr_t)Memory::physical_base + 0x100010000)
    return BackPatch((u32)(access_address - (uintptr_t)Memory::physical_base), ctx);
  if (access_address >= (uintptr_t)Memory::logical_base &&
      access_address < (uintptr_t)Memory::logical_base + 0x100010000)
    return BackPatch((u32)(access_address - (uintptr_t)Memory::logical_base), ctx);

  return false;
}

bool Jitx86Base::BackPatch(u32 emAddress, SContext* ctx)
{
  u8* codePtr = (u8*)ctx->CTX_PC;

  if (!IsInSpace(codePtr))
    return false;  // this will become a regular crash real soon after this

  auto it = backPatchInfo.find(codePtr);
  if (it == backPatchInfo.end())
  {
    PanicAlert("BackPatch: no register use entry for address %p", codePtr);
    return false;
  }

  TrampolineInfo& info = it->second;

  u8* exceptionHandler = nullptr;
  if (jit->jo.memcheck)
  {
    auto it2 = exceptionHandlerAtLoc.find(codePtr);
    if (it2 != exceptionHandlerAtLoc.end())
      exceptionHandler = it2->second;
  }

  // In the trampoline code, we jump back into the block at the beginning
  // of the next instruction. The next instruction comes immediately
  // after the backpatched operation, or BACKPATCH_SIZE bytes after the start
  // of the backpatched operation, whichever comes last. (The JIT inserts NOPs
  // into the original code if necessary to ensure there is enough space
  // to insert the backpatch jump.)

  jit->js.generatingTrampoline = true;
  jit->js.trampolineExceptionHandler = exceptionHandler;

  // Generate the trampoline.
  const u8* trampoline = trampolines.GenerateTrampoline(info);
  jit->js.generatingTrampoline = false;
  jit->js.trampolineExceptionHandler = nullptr;

  u8* start = info.start;

  // Patch the original memory operation.
  XEmitter emitter(start);
  emitter.JMP(trampoline, true);
  // NOPs become dead code
  const u8* end = info.start + info.len;
  for (const u8* i = emitter.GetCodePtr(); i < end; ++i)
    emitter.INT3();

  // Rewind time to just before the start of the write block. If we swapped memory
  // before faulting (eg: the store+swap was not an atomic op like MOVBE), let's
  // swap it back so that the swap can happen again (this double swap isn't ideal but
  // only happens the first time we fault).
  if (info.nonAtomicSwapStoreSrc != INVALID_REG)
  {
    u64* ptr = ContextRN(ctx, info.nonAtomicSwapStoreSrc);
    switch (info.accessSize << 3)
    {
    case 8:
      // No need to swap a byte
      break;
    case 16:
      *ptr = Common::swap16(static_cast<u16>(*ptr));
      break;
    case 32:
      *ptr = Common::swap32(static_cast<u32>(*ptr));
      break;
    case 64:
      *ptr = Common::swap64(static_cast<u64>(*ptr));
      break;
    default:
      _dbg_assert_(DYNA_REC, 0);
      break;
    }
  }

  // This is special code to undo the LEA in SafeLoadToReg if it clobbered the address
  // register in the case where reg_value shared the same location as opAddress.
  if (info.offsetAddedToAddress)
  {
    u64* ptr = ContextRN(ctx, info.op_arg.GetSimpleReg());
    *ptr -= static_cast<u32>(info.offset);
  }

  ctx->CTX_PC = reinterpret_cast<u64>(trampoline);

  return true;
}
