// Do not edit this file

/**
 * Usage (after running make): make run or ./main memory.txt (must supply 1 CLI
 * argument).
 *
 * Main file for a cache simulator that implements the two cache mapping
 * functions (CMF): 1) direct mapping (DM) 2) fully associative (FA)
 *
 * This is an interactive simulator that allows the user to
 * 1) Select the cache mapping function (CMF)
 * 2) Enter hex memory addresses
 * 3) For the CMF selected, determine if the address results in a cache hit or
 * miss and display to the user 4) If a cache miss, determine the cache line to
 * be replaced for the chosen CMF. For FA, the replacement algorithm is least
 * frequently used (LFU) based on the minimum number of cache hits.
 */

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "cache.h"
#include "memory.h"

char* CMFS[] = {"Direct Mapping", "Fully Associative"};
char* OP[] = {"MISS", "HIT"};
char* ROP[] = {"No Replacement", "Replacement"};

extern unsigned int addr_bits;

int main(int argc, char* argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: ./main memory.txt\n");
        exit(1);
    }

    unsigned int num_blocks;
    unsigned int cmf;
    unsigned int hex_addr;

    int byte;
    bool found, replace;

    addr_bits = read_memory_file(argv[1]);

    if (addr_bits != FAIL) {
        /*
           ---------------------------------------------------
           STEP 1:
           ---------------------------------------------------
           Calculate the number of blocks then display to the
           user the number of physical memory addressable bits
           and the total number of physical memory blocks.
        */

        printf("\n------------------------\n");
        printf("[STEP 1] Setting up physical memory\n");
        printf("------------------------\n");

        num_blocks = number_of_blocks(addr_bits, NUM_BLOCK_OFFSET_BITS);

        printf(
            "Physical memory addressable bits = %d, total number of blocks = "
            "%d\n",
            addr_bits, num_blocks);

        /*
           ---------------------------------------------------
           STEP 2:
           ---------------------------------------------------
           Initialize the block points, i.e. determine the physical
           memory starting address for each block.

        */

        printf("\n------------------------\n");
        printf("[STEP 2] Determining physical memory block locations\n");
        printf("------------------------\n");

        initialize_block_pointers(num_blocks, NUM_BLOCK_OFFSET_BITS);

        /*
           ---------------------------------------------------
           STEP 3:
           ---------------------------------------------------
           Ask the user to choose the cache mapping function (CMF)

        */

        printf("\n------------------------\n");
        printf("[STEP 3] Select cache mapping function (CMF)\n");
        printf("------------------------\n");
        printf("1 = Direct mapping\n");
        printf("2 = Fully associative\n");
        printf("------------------------\n");
        printf("Please enter 1 or 2: ");

        scanf("%d", &cmf);

        if (cmf != DM && cmf != FA) {
            printf("Unknown CMF mode (%d) ... exiting\n", cmf);
            exit(1);
        } else {
            /*
               ---------------------------------------------------
               STEP 4:
               ---------------------------------------------------
               Initialize cache, that is, all the cache lines should
               be empty (i.e. no block of physical memory is loaded
               into any cache line).

            */

            printf("\n------------------------\n");
            printf("[STEP 4] initializing cache\n");
            printf("------------------------\n");

            initialize_cache(NUM_LINES);

            /*
               ---------------------------------------------------
               STEP 5:
               ---------------------------------------------------
               Ask the user to enter physical a memory address location
               in hexadecimal format (e.g. EA), then get and display
               physical memory value from cache and display to user.

            */

            printf("\n------------------------\n");
            printf("[STEP 5] Starting simulation\n");
            printf("------------------------\n");
            printf("CMF is %s\n", CMFS[cmf - 1]);
            printf("To exit simulation, press Ctrl+C\n");

            while (true) {
                printf("\nPlease enter %d-bit hexadecimal address: ",
                       addr_bits);
                scanf("%x", &hex_addr);

                printf(
                    "Entered Hexadecimal (Base-16) address %02X (Base-10 value "
                    "%d)\n",
                    hex_addr, hex_addr);

                byte = cread(cmf, &hex_addr, &found, &replace);

                if (byte != FAIL) {
                    printf(
                        "[%s:%s] The byte value at memory address %02X is "
                        "%02X\n",
                        OP[found], ROP[replace], hex_addr, byte);
                } else {
                    printf("Failed to read cache for memory location %02X\n",
                           hex_addr);
                }
            }
        }
    } else {
        printf("Unable to read the memory file ( %s ) ... exiting\n", argv[1]);
    }

    return OK;
}