/**
 * University of Illinois/NCSA
 * Open Source License
 *
 *  Copyright (c) 2007-2008,The Board of Trustees of the University of
 *  Illinois.  All rights reserved.
 *
 *  Copyright (c) 2009 Sam King
 *
 *  Developed by:
 *
 *  Professor Sam King in the Department of Computer Science
 *  The University of Illinois at Urbana-Champaign
 *      http://www.cs.uiuc.edu/homes/kingst/Research.html
 *
 *       Permission is hereby granted, free of charge, to any person
 *       obtaining a copy of this software and associated
 *       documentation files (the "Software"), to deal with the
 *       Software without restriction, including without limitation
 *       the rights to use, copy, modify, merge, publish, distribute,
 *       sublicense, and/or sell copies of the Software, and to permit
 *       persons to whom the Software is furnished to do so, subject
 *       to the following conditions:
 *
 *          Redistributions of source code must retain the above
 *          copyright notice, this list of conditions and the
 *          following disclaimers.
 *
 *          Redistributions in binary form must reproduce the above
 *          copyright notice, this list of conditions and the
 *          following disclaimers in the documentation and/or other
 *          materials provided with the distribution.
 *
 *          Neither the names of Sam King, the University of Illinois,
 *          nor the names of its contributors may be used to endorse
 *          or promote products derived from this Software without
 *          specific prior written permission.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT.  IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS WITH THE SOFTWARE.
*/
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <string.h>

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

using namespace std;

#define RAM_SIZE 0x20000
#define NUM_REGS 32

#define OP_FORMAT_1     0x1
#define OP_FORMAT_2     0x0
#define OP_FORMAT_3_ALU 0x2
#define OP_FORMAT_3_MEM 0x3

#define OP2_BRANCH      0x2
#define OP2_SETHI       0x4

#define OP3_ADD         0x0
#define OP3_SUB         0x4
#define OP3_SUBCC       0x14
#define OP3_JMPL        0x38

#define OP3_LD          0x0
#define OP3_ST          0x4

#define COND_BNE        0x9
#define COND_B          0x8
#define COND_BE         0x1

int PRINT_INST = 0;

#define COUT(x) do { if(PRINT_INST) { x; } } while(0)

typedef uint32_t u32;
typedef int32_t i32;

struct CPU {
    u32 regs[NUM_REGS];
    u32 pc;
    u32 branch_pc;
    bool icc_z;    
    bool icc_n;
};

struct control_signals {
    u32 op;
    u32 rd;
    u32 cond;
    u32 op2;
    u32 op3;
    u32 rs1;
    u32 i;
    u32 asi;
    i32 simm;
    u32 imm;
    u32 disp22;
    u32 disp30;
    u32 rs2;
    u32 raw;
};

static uint8_t *ram = NULL;
static bool userQuit = false;
static time_t startTime;

void ctrlC(int /*signo*/) {
    userQuit = true;
}

u32 fetch(struct CPU */*cpu*/) {
    u32 opcode = 0;
    // XXX FILL THIS IN
    return opcode;
}

i32 sign_extend_22(u32 disp22) {
    i32 ret;

    ret = disp22;    
    assert((disp22 & 0xffc00000) == 0);
    if((disp22 & 0x200000) != 0) {
        ret |= 0xffc00000;
    }

    return ret;
}

i32 sign_extend_13(u32 imm) {
    i32 ret;

    ret = imm;    
    assert((imm & 0xffffe000) == 0);
    if((imm & 0x0100) != 0) {
        ret |= 0xffffe000;
    }

    return ret;
}

void decode_control_signals(u32 opcode, struct control_signals *control) {
    control->raw = opcode;
    control->op = (opcode >> 30) & 0x3;
    control->rd = (opcode >> 25) & 0x1f;
    control->op2 = (opcode >> 22) & 0x7;
    control->op3 = (opcode >> 19) & 0x3f;
    control->rs1 = (opcode >> 14) & 0x1f;
    control->i = (opcode >> 13) & 0x1;
    control->asi = (opcode >> 5) & 0xff;
    control->simm = sign_extend_13(opcode & 0x1fff);
    control->imm = opcode & 0x3fffff;
    control->rs2 = opcode & 0x1f;
    control->disp22 = opcode & 0x3fffff;
    control->disp30 = opcode & 0x3fffffff;
    control->cond = (opcode >> 25) & 0xf;
}

void addi(struct CPU */*cpu*/, u32 /*rd*/, u32 /*rs1*/, i32 /*signedImm*/) {
    // XXX FILL THIS IN
}


bool execute(struct CPU */*cpu*/, struct control_signals */*control*/) {
    // XXX FILL THIS IN
    return true;
}

string prompt(const char *str) {
    string ret;
    cout << str;
    cout.flush();
    cin >> ret;
    return ret;
}

void fillState(char *fileName, void *buf, int size) {
    int ret, fd;

    assert(size > 0);

    fd = open(fileName, O_RDONLY);
    assert(fd >= 0);

    ret = read(fd, buf, size);
    assert((ret == size) && (ret>0));

    close(fd);
}

void cpu_exec(struct CPU *cpu) {
    struct control_signals *control = new struct control_signals;
    u32 opcode;
    bool runSimulation = true;

    while(runSimulation && !userQuit) {
        opcode = fetch(cpu);
        decode_control_signals(opcode, control);
        
        // second part of decode and write back also
        runSimulation = execute(cpu, control);
    }

    if(userQuit) {

    }
}

int main(int argc, char *argv[]) {
    ifstream infile;
    struct CPU *cpu;

    if(argc < 2) {
        cerr << "Usage: " << argv[0] << " memory_file [cpu_file]" << endl;
        return -1;
    }

    signal(SIGINT, ctrlC);

    // initialize our state
    ram = new uint8_t[RAM_SIZE];
    cpu = new struct CPU;
    for(int idx = 0; idx < NUM_REGS; idx++) {
        cpu->regs[idx] = 0;
    }
    cpu->pc = 0;
    cpu->branch_pc = (u32) -1;
    // setup the stack pointer
    cpu->regs[14] = RAM_SIZE-4-120;

    // setup the fib program
    cpu->regs[8] = RAM_SIZE-4;

    // fetch our memory image and cpu state (if set)
    fillState(argv[1], ram, RAM_SIZE);
    if(argc >= 3) {        
        fillState(argv[2], cpu, sizeof(struct CPU));
    }
    
    startTime = time(NULL);
    cpu_exec(cpu);

    return 0;
}