// File: daniel5151[P]mips241/src/main.cc
// Function: main
int main(int argc, char const *argv[]) {
  // Check arguments
  if (argc < 2 || argc > 4) {
    std::cerr
      << "Usage: ./mips241 <filename> [--cin] [--twoints] [--debug]"
      << std::endl
      << std::endl
      << "  By default, mips241 operates in `mips.array` mode."    << std::endl
      << "  To switch to `mips.twoints` mode, pass in [--twoints]" << std::endl
      << std::endl
      << "    --cin     : Read program from cin"          << std::endl
      << "    --twoints : operate in `mips.twoints` mode" << std::endl
      << "    --debug   : enable interactive debug mode"  << std::endl
      << std::endl
      << "  Known issues:"  << std::endl
      << "    - Don't use [--debug] in conjunction with [--cin], it won't work." << std::endl
      << "    - [--cin] mode doesn't let you input initial register values." << std::endl
      << "        It's not that it's impossible, i'm just lazy and did not" << std::endl
      << "        implement it yet :)" << std::endl
      << std::endl
      << std::endl;
    return -1;
  }

  std::string filename;
  bool use_debug_mode = false;
  bool use_twoints = false;
  bool use_cin = false;
  for (int i = 1; i < argc; i++) {
    std::string arg = string(argv[i]);

    if (arg == "--twoints") use_twoints = true;
    if (arg == "--debug" ) use_debug_mode = true;
    if (arg == "--cin") use_cin = true;

    if (i == 1) filename = arg;
  }

  //--------------------------------- SETUP ---------------------------------//

  // Setup and connect up System
  MIPS::RAM ram;
  MIPS::BUS bus (ram);
  MIPS::CPU cpu (bus);

  // Open file
  std::ifstream f (filename, std::ifstream::binary);
  if (!f.is_open() && !use_cin) {
    std::cerr << "Cannot open file `" << filename << "`" << std::endl;
    return -1;
  }

  std::istream &bin = (use_cin) ? std::cin : f;

  // Insert executable into virtual memory

  uint32_t memaddr = 0x0; // base memaddr

  char word[4];
  for ever {
    bin.read(word, 4);

    if(bin.eof()) break;

    // Reverse endiannessss
    char x;
    x = word[0];
    word[0] = word[3];
    word[3] = x;
    x = word[1];
    word[1] = word[2];
    word[2] = x;

    // Store it in RAM (with some funky casting)
    ram.store(memaddr, *(uint32_t *)word );

    memaddr += 4;
  }

  // Accept user input
  if (use_twoints) {
    // Let user input 2 integers
    std::cerr << "Enter value for Register 1: ";
    cpu.setRegister(1, readNum(std::cin));
    std::cerr << "Enter value for Register 2: ";
    cpu.setRegister(2, readNum(std::cin));
  } else {
    // Let user enter Array of values
    std::cerr << "Enter length of Array: ";
    int arr_length = readNum(std::cin);
    cpu.setRegister(2, arr_length); // $2 = length of array

    // Start populating the array 16 words after the end of the program
    // this was chosen relatively arbitrarilly...
    memaddr += 0x10;

    cpu.setRegister(1, memaddr); // $1 = Pointer to base of array

    for (int i = 0; i < arr_length; i++) {
      std::cerr << "Enter Array element " << i << ": ";
      ram.store(memaddr, readNum(std::cin));
      memaddr += 4;
    }
  }

  // If this program is running interactively, clear cin before starting cpu
  //  emulation
  bool isTTY = isatty(fileno(stdin));
  if (isTTY) { std::string dummy; getline(std::cin, dummy); }


  std::cerr << std::endl;

  // Start CPU execution
  MIPS::Debugger debugger (cpu, ram, bus);

  try {
    std::cerr << "Starting CPU..." << std::endl;
    do {
      if (use_debug_mode) debugger.debugREPL();

      // Execute cpu cycle
      cpu.do_cycle();

      std::cout << bus.getOutput();

    } while (cpu.stillExecuting());

    std::cerr
      << "Execution completed successfully!"
      << std::endl;
    debugger.printCPUState();

  } catch (const std::string & msg) {
    std::cerr
      << "ERROR : "
      << msg
      << std::endl;
    debugger.printCPUState();
    return -1;
  }

  return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x5b0, %rsp            # imm = 0x5B0
movl $0x0, -0x4(%rbp)
movl %edi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
cmpl $0x2, -0x8(%rbp)
jl 0xcce9
cmpl $0x4, -0x8(%rbp)
jle 0xce8e
movq 0xa300(%rip), %rdi      # 0x16ff0
leaq 0x37ce(%rip), %rsi      # 0x104c5
callq 0x3250
movq %rax, %rdi
movq 0xa2a2(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
movq 0xa293(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x37db(%rip), %rsi      # 0x104ff
callq 0x3250
movq %rax, %rdi
movq 0xa275(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x37f2(%rip), %rsi      # 0x10534
callq 0x3250
movq %rax, %rdi
movq 0xa257(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
movq 0xa248(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x37fd(%rip), %rsi      # 0x1056c
callq 0x3250
movq %rax, %rdi
movq 0xa22a(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x3805(%rip), %rsi      # 0x10592
callq 0x3250
movq %rax, %rdi
movq 0xa20c(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x3816(%rip), %rsi      # 0x105c1
callq 0x3250
movq %rax, %rdi
movq 0xa1ee(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
movq 0xa1df(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x3817(%rip), %rsi      # 0x105ef
callq 0x3250
movq %rax, %rdi
movq 0xa1c1(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x3809(%rip), %rsi      # 0x105ff
callq 0x3250
movq %rax, %rdi
movq 0xa1a3(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x3831(%rip), %rsi      # 0x10645
callq 0x3250
movq %rax, %rdi
movq 0xa185(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x3855(%rip), %rsi      # 0x10687
callq 0x3250
movq %rax, %rdi
movq 0xa167(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
leaq 0x3878(%rip), %rsi      # 0x106c8
callq 0x3250
movq %rax, %rdi
movq 0xa149(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
movq 0xa13a(%rip), %rsi      # 0x16fa8
callq 0x32a0
movq %rax, %rdi
movq 0xa12b(%rip), %rsi      # 0x16fa8
callq 0x32a0
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
jmp 0xd720
leaq -0x30(%rbp), %rdi
callq 0x33d0
movb $0x0, -0x31(%rbp)
movb $0x0, -0x32(%rbp)
movb $0x0, -0x33(%rbp)
movl $0x1, -0x38(%rbp)
movl -0x38(%rbp), %eax
cmpl -0x8(%rbp), %eax
jge 0xcfd7
movq -0x10(%rbp), %rax
movslq -0x38(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movq %rax, -0x528(%rbp)
leaq -0x59(%rbp), %rdi
movq %rdi, -0x520(%rbp)
callq 0x34d0
movq -0x528(%rbp), %rsi
movq -0x520(%rbp), %rdx
leaq -0x58(%rbp), %rdi
callq 0x3a90
jmp 0xcef2
leaq -0x59(%rbp), %rdi
callq 0x32d0
leaq 0x37e2(%rip), %rsi      # 0x106e4
leaq -0x58(%rbp), %rdi
callq 0x5ee0
movb %al, -0x529(%rbp)
jmp 0xcf13
movb -0x529(%rbp), %al
testb $0x1, %al
jne 0xcf1f
jmp 0xcf59
movb $0x1, -0x32(%rbp)
jmp 0xcf59
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
leaq -0x59(%rbp), %rdi
callq 0x32d0
jmp 0xd715
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
leaq -0x58(%rbp), %rdi
callq 0x3528
jmp 0xd715
leaq 0x378e(%rip), %rsi      # 0x106ee
leaq -0x58(%rbp), %rdi
callq 0x5ee0
movb %al, -0x52a(%rbp)
jmp 0xcf71
movb -0x52a(%rbp), %al
testb $0x1, %al
jne 0xcf7d
jmp 0xcf81
movb $0x1, -0x31(%rbp)
leaq 0x376e(%rip), %rsi      # 0x106f6
leaq -0x58(%rbp), %rdi
callq 0x5ee0
movb %al, -0x52b(%rbp)
jmp 0xcf99
movb -0x52b(%rbp), %al
testb $0x1, %al
jne 0xcfa5
jmp 0xcfa9
movb $0x1, -0x33(%rbp)
cmpl $0x1, -0x38(%rbp)
jne 0xcfc0
leaq -0x30(%rbp), %rdi
leaq -0x58(%rbp), %rsi
callq 0x3040
jmp 0xcfbe
jmp 0xcfc0
leaq -0x58(%rbp), %rdi
callq 0x3528
movl -0x38(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x38(%rbp)
jmp 0xceaa
leaq -0xa0(%rbp), %rdi
callq 0xd740
jmp 0xcfe5
leaq -0x118(%rbp), %rdi
leaq -0xa0(%rbp), %rsi
callq 0x3720
jmp 0xcffa
leaq -0x1d0(%rbp), %rdi
leaq -0x118(%rbp), %rsi
callq 0x52a0
jmp 0xd00f
leaq -0x3d8(%rbp), %rdi
leaq -0x30(%rbp), %rsi
movl $0x4, %edx
callq 0x3440
jmp 0xd026
leaq -0x3d8(%rbp), %rdi
callq 0x3430
movb %al, -0x52c(%rbp)
jmp 0xd03a
movb -0x52c(%rbp), %al
testb $0x1, %al
jne 0xd125
jmp 0xd04a
testb $0x1, -0x33(%rbp)
jne 0xd125
movq 0x9f95(%rip), %rdi      # 0x16ff0
leaq 0x369a(%rip), %rsi      # 0x106fc
callq 0x3250
movq %rax, -0x538(%rbp)
jmp 0xd070
movq -0x538(%rbp), %rdi
leaq -0x30(%rbp), %rsi
callq 0x3220
movq %rax, -0x540(%rbp)
jmp 0xd089
movq -0x540(%rbp), %rdi
leaq 0x3676(%rip), %rsi      # 0x1070d
callq 0x3250
movq %rax, -0x548(%rbp)
jmp 0xd0a5
movq -0x548(%rbp), %rdi
movq 0x9ef5(%rip), %rsi      # 0x16fa8
callq 0x32a0
jmp 0xd0ba
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
movl $0x1, -0x3dc(%rbp)
jmp 0xd6aa
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
jmp 0xd715
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
jmp 0xd709
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
jmp 0xd6fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
jmp 0xd6f1
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
jmp 0xd6e5
testb $0x1, -0x33(%rbp)
je 0xd13b
movq 0x9ea6(%rip), %rax      # 0x16fd8
movq %rax, -0x550(%rbp)
jmp 0xd14b
leaq -0x3d8(%rbp), %rax
movq %rax, -0x550(%rbp)
jmp 0xd14b
movq -0x550(%rbp), %rax
movq %rax, -0x3e8(%rbp)
movl $0x0, -0x3ec(%rbp)
movq -0x3e8(%rbp), %rdi
leaq -0x3f0(%rbp), %rsi
movl $0x4, %edx
callq 0x3370
jmp 0xd17d
movq -0x3e8(%rbp), %rdi
movq (%rdi), %rax
movq -0x18(%rax), %rax
addq %rax, %rdi
callq 0x34f0
movb %al, -0x551(%rbp)
jmp 0xd19b
movb -0x551(%rbp), %al
testb $0x1, %al
jne 0xd1a7
jmp 0xd1a9
jmp 0xd21f
movb -0x3f0(%rbp), %al
movb %al, -0x3f1(%rbp)
movb -0x3ed(%rbp), %al
movb %al, -0x3f0(%rbp)
movb -0x3f1(%rbp), %al
movb %al, -0x3ed(%rbp)
movb -0x3ef(%rbp), %al
movb %al, -0x3f1(%rbp)
movb -0x3ee(%rbp), %al
movb %al, -0x3ef(%rbp)
movb -0x3f1(%rbp), %al
movb %al, -0x3ee(%rbp)
movl -0x3ec(%rbp), %esi
movl -0x3f0(%rbp), %edx
leaq -0xa0(%rbp), %rdi
callq 0xd7f0
jmp 0xd20b
movl -0x3ec(%rbp), %eax
addl $0x4, %eax
movl %eax, -0x3ec(%rbp)
jmp 0xd163
testb $0x1, -0x32(%rbp)
je 0xd2b2
movq 0x9dc0(%rip), %rdi      # 0x16ff0
leaq 0x34d8(%rip), %rsi      # 0x1070f
callq 0x3250
jmp 0xd23e
movq 0x9d93(%rip), %rdi      # 0x16fd8
callq 0xcad0
movl %eax, -0x558(%rbp)
jmp 0xd252
movl -0x558(%rbp), %edx
leaq -0x1d0(%rbp), %rdi
movl $0x1, %esi
callq 0x53a0
jmp 0xd26b
movq 0x9d7e(%rip), %rdi      # 0x16ff0
leaq 0x34b3(%rip), %rsi      # 0x1072c
callq 0x3250
jmp 0xd280
movq 0x9d51(%rip), %rdi      # 0x16fd8
callq 0xcad0
movl %eax, -0x55c(%rbp)
jmp 0xd294
movl -0x55c(%rbp), %edx
leaq -0x1d0(%rbp), %rdi
movl $0x2, %esi
callq 0x53a0
jmp 0xd2ad
jmp 0xd3ef
movq 0x9d37(%rip), %rdi      # 0x16ff0
leaq 0x3489(%rip), %rsi      # 0x10749
callq 0x3250
jmp 0xd2c7
movq 0x9d0a(%rip), %rdi      # 0x16fd8
callq 0xcad0
movl %eax, -0x560(%rbp)
jmp 0xd2db
movl -0x560(%rbp), %eax
movl %eax, -0x3f8(%rbp)
movl -0x3f8(%rbp), %edx
leaq -0x1d0(%rbp), %rdi
movl $0x2, %esi
callq 0x53a0
jmp 0xd300
movl -0x3ec(%rbp), %eax
addl $0x10, %eax
movl %eax, -0x3ec(%rbp)
movl -0x3ec(%rbp), %edx
leaq -0x1d0(%rbp), %rdi
movl $0x1, %esi
callq 0x53a0
jmp 0xd328
movl $0x0, -0x3fc(%rbp)
movl -0x3fc(%rbp), %eax
cmpl -0x3f8(%rbp), %eax
jge 0xd3ed
movq 0x9ca5(%rip), %rdi      # 0x16ff0
leaq 0x340f(%rip), %rsi      # 0x10761
callq 0x3250
movq %rax, -0x568(%rbp)
jmp 0xd360
movq -0x568(%rbp), %rdi
movl -0x3fc(%rbp), %esi
callq 0x34a0
movq %rax, -0x570(%rbp)
jmp 0xd37b
movq -0x570(%rbp), %rdi
leaq 0x3425(%rip), %rsi      # 0x107ae
callq 0x3250
jmp 0xd390
movl -0x3ec(%rbp), %eax
movl %eax, -0x578(%rbp)
movq 0x9c35(%rip), %rdi      # 0x16fd8
callq 0xcad0
movl %eax, -0x574(%rbp)
jmp 0xd3b0
movl -0x574(%rbp), %edx
movl -0x578(%rbp), %esi
leaq -0xa0(%rbp), %rdi
callq 0xd7f0
jmp 0xd3ca
movl -0x3ec(%rbp), %eax
addl $0x4, %eax
movl %eax, -0x3ec(%rbp)
movl -0x3fc(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3fc(%rbp)
jmp 0xd332
jmp 0xd3ef
movq 0x9bca(%rip), %rax      # 0x16fc0
movq (%rax), %rdi
callq 0x32c0
movl %eax, %edi
callq 0x34b0
cmpl $0x0, %eax
setne %al
andb $0x1, %al
movb %al, -0x3fd(%rbp)
testb $0x1, -0x3fd(%rbp)
je 0xd46f
leaq -0x420(%rbp), %rdi
movq %rdi, -0x580(%rbp)
callq 0x33d0
movq -0x580(%rbp), %rsi
movq 0x9b9b(%rip), %rdi      # 0x16fd8
callq 0x3080
jmp 0xd444
leaq -0x420(%rbp), %rdi
callq 0x3528
jmp 0xd46f
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
leaq -0x420(%rbp), %rdi
callq 0x3528
jmp 0xd6e5
movq 0x9b7a(%rip), %rdi      # 0x16ff0
movq 0x9b2b(%rip), %rsi      # 0x16fa8
callq 0x32a0
jmp 0xd484
leaq -0x4f0(%rbp), %rdi
leaq -0x1d0(%rbp), %rsi
leaq -0xa0(%rbp), %rdx
leaq -0x118(%rbp), %rcx
callq 0x5f10
jmp 0xd4a7
movq 0x9b42(%rip), %rdi      # 0x16ff0
leaq 0x32c1(%rip), %rsi      # 0x10776
callq 0x3250
movq %rax, -0x588(%rbp)
jmp 0xd4c3
movq -0x588(%rbp), %rdi
movq 0x9ad7(%rip), %rsi      # 0x16fa8
callq 0x32a0
jmp 0xd4d8
jmp 0xd4da
testb $0x1, -0x31(%rbp)
je 0xd501
leaq -0x4f0(%rbp), %rdi
callq 0x7640
jmp 0xd4ee
jmp 0xd501
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
jmp 0xd5c5
leaq -0x1d0(%rbp), %rdi
callq 0x5780
jmp 0xd50f
leaq -0x510(%rbp), %rdi
leaq -0x118(%rbp), %rsi
callq 0x39e0
jmp 0xd524
movq 0x9a9d(%rip), %rdi      # 0x16fc8
leaq -0x510(%rbp), %rsi
callq 0x3220
jmp 0xd539
leaq -0x510(%rbp), %rdi
callq 0x3528
leaq -0x1d0(%rbp), %rdi
callq 0x5380
movb %al, -0x589(%rbp)
jmp 0xd559
movb -0x589(%rbp), %al
testb $0x1, %al
jne 0xd4da
jmp 0xd569
movq 0x9a80(%rip), %rdi      # 0x16ff0
leaq 0x320f(%rip), %rsi      # 0x10786
callq 0x3250
movq %rax, -0x598(%rbp)
jmp 0xd585
movq -0x598(%rbp), %rdi
movq 0x9a15(%rip), %rsi      # 0x16fa8
callq 0x32a0
jmp 0xd59a
leaq -0x4f0(%rbp), %rdi
callq 0x6300
jmp 0xd5a8
jmp 0xd67d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
leaq -0x510(%rbp), %rdi
callq 0x3528
movl -0x6c(%rbp), %eax
movl $0x1, %ecx
cmpl %ecx, %eax
jne 0xd69c
movq -0x68(%rbp), %rdi
callq 0x3100
movq %rax, -0x518(%rbp)
movq 0x9a04(%rip), %rdi      # 0x16ff0
leaq 0x31b5(%rip), %rsi      # 0x107a8
callq 0x3250
movq %rax, -0x5a0(%rbp)
jmp 0xd601
movq -0x5a0(%rbp), %rdi
movq -0x518(%rbp), %rsi
callq 0x3220
movq %rax, -0x5a8(%rbp)
jmp 0xd61d
movq -0x5a8(%rbp), %rdi
movq 0x997d(%rip), %rsi      # 0x16fa8
callq 0x32a0
jmp 0xd632
leaq -0x4f0(%rbp), %rdi
callq 0x6300
jmp 0xd640
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
movl $0x1, -0x3dc(%rbp)
callq 0x3460
jmp 0xd66b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
callq 0x3460
jmp 0xd67b
jmp 0xd68e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x68(%rbp)
movl %eax, -0x6c(%rbp)
jmp 0xd69c
jmp 0xd69c
movl $0x0, -0x4(%rbp)
movl $0x1, -0x3dc(%rbp)
leaq -0x4f0(%rbp), %rdi
callq 0x6020
jmp 0xd6aa
leaq -0x4f0(%rbp), %rdi
callq 0x6020
jmp 0xd6e5
leaq -0x3d8(%rbp), %rdi
callq 0x30b0
leaq -0x1d0(%rbp), %rdi
callq 0x5370
leaq -0x118(%rbp), %rdi
callq 0x37c0
leaq -0xa0(%rbp), %rdi
callq 0xd760
leaq -0x30(%rbp), %rdi
callq 0x3528
jmp 0xd720
leaq -0x3d8(%rbp), %rdi
callq 0x30b0
leaq -0x1d0(%rbp), %rdi
callq 0x5370
leaq -0x118(%rbp), %rdi
callq 0x37c0
leaq -0xa0(%rbp), %rdi
callq 0xd760
leaq -0x30(%rbp), %rdi
callq 0x3528
jmp 0xd72c
movl -0x4(%rbp), %eax
addq $0x5b0, %rsp            # imm = 0x5B0
popq %rbp
retq
movq -0x68(%rbp), %rdi
callq 0x34c0
movq %rax, %rdi
callq 0x41f0
nopl (%rax)