//
// vr4300/icache.c: VR4300 instruction cache.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//

#include "common.h"
#include "vr4300/icache.h"

static inline struct vr4300_icache_line* get_line(
  struct vr4300_icache *icache, uint64_t vaddr);
static inline const struct vr4300_icache_line* get_line_const(
  const struct vr4300_icache *icache, uint64_t vaddr);

static inline uint32_t get_tag(const struct vr4300_icache_line *line);
static inline bool is_valid(const struct vr4300_icache_line *line);

static void invalidate_line(struct vr4300_icache_line *line);
static void set_taglo(struct vr4300_icache_line *line, uint32_t taglo);
static void validate_line(struct vr4300_icache_line *line, uint32_t tag);

// Returns the line for a given virtual address.
struct vr4300_icache_line* get_line(
  struct vr4300_icache *icache, uint64_t vaddr) {
  return icache->lines + (vaddr >> 5 & 0x1FF);
}

// Returns the line for a given virtual address.
const struct vr4300_icache_line* get_line_const(
  const struct vr4300_icache *icache, uint64_t vaddr) {
  return icache->lines + (vaddr >> 5 & 0x1FF);
}

// Returns the physical tag associated with the line.
uint32_t get_tag(const struct vr4300_icache_line *line) {
  return line->metadata & ~0xFFFU;
}

// Invalidates the line, but leaves the physical tag untouched.
void invalidate_line(struct vr4300_icache_line *line) {
  line->metadata &= ~0x1;
}

// Returns true if the line is valid, otherwise returns false.
bool is_valid(const struct vr4300_icache_line *line) {
  return (line->metadata & 0x1) == 0x1;
}

// Sets the tag of the specified line and valid bit.
void set_taglo(struct vr4300_icache_line *line, uint32_t taglo) {
  line->metadata = (taglo << 4 & 0xFFFFF000) | (taglo >> 7 & 0x1);
}

// Sets the line's physical tag and validates the line.
static void validate_line(struct vr4300_icache_line *line, uint32_t tag) {
  line->metadata = tag | 0x1;
}

// Fills an instruction cache line with data.
void vr4300_icache_fill(struct vr4300_icache *icache,
  uint64_t vaddr, uint32_t paddr, const void *data) {
  struct vr4300_icache_line *line = get_line(icache, vaddr);

  memcpy(line->data, data, sizeof(line->data));
  validate_line(line, paddr & ~0xFFFU);
}

// Returns the tag of the line associated with vaddr.
uint32_t vr4300_icache_get_tag(const struct vr4300_icache *icache,
  uint64_t vaddr) {
  const struct vr4300_icache_line *line = get_line_const(icache, vaddr);

  return get_tag(line) | (vaddr & 0xFE0);
}

// Initializes the instruction cache.
void vr4300_icache_init(struct vr4300_icache *icache) {
}

// Invalidates an instruction cache line (regardless if hit or miss).
void vr4300_icache_invalidate(struct vr4300_icache *icache, uint64_t vaddr) {
  struct vr4300_icache_line *line = get_line(icache, vaddr);

  invalidate_line(line);
}

// Invalidates an instruction cache line (only on a hit).
void vr4300_icache_invalidate_hit(struct vr4300_icache *icache,
  uint64_t vaddr, uint32_t paddr) {
  struct vr4300_icache_line *line = get_line(icache, vaddr);
  uint32_t ptag = get_tag(line);

  if (ptag == (paddr & ~0xFFFU) && is_valid(line))
    invalidate_line(line);
}

// Probes the instruction cache for a matching line.
const struct vr4300_icache_line* vr4300_icache_probe(
  const struct vr4300_icache *icache, uint64_t vaddr, uint32_t paddr) {
  const struct vr4300_icache_line *line = get_line_const(icache, vaddr);
  uint32_t ptag = get_tag(line);

  // Virtually index, and physically tagged.
  if (ptag == (paddr & ~0xFFFU) && is_valid(line))
    return line;

  return NULL;
}

// Sets the physical tag associated with the line.
void vr4300_icache_set_taglo(struct vr4300_icache *icache,
  uint64_t vaddr, uint32_t taglo) {
  struct vr4300_icache_line *line = get_line(icache, vaddr);

  set_taglo(line, taglo);
}

