// File: CMU-SAFARI[P]ramulator/src/SALP.cpp
// Function: ramulator::SALP::init_prereq()
void SALP::init_prereq()
{
    prereq[int(Level::Rank)][int(Command::RD)] = [] (DRAM<SALP>* node, Command cmd, int id) {
        switch (int(node->state)) {
            case int(State::PowerUp): return Command::MAX;
            case int(State::ActPowerDown): return Command::PDX;
            case int(State::PrePowerDown): return Command::PDX;
            case int(State::SelfRefresh): return Command::SRX;
            default: assert(false);
        }};
    prereq[int(Level::Rank)][int(Command::WR)] = prereq[int(Level::Rank)][int(Command::RD)];

    switch(int(type)){
        case int(Type::SALP_1):
            prereq[int(Level::Bank)][int(Command::RD)] = [] (DRAM<SALP>* node, Command cmd, int id) {
                switch (int(node->state)){
                    case int(State::Closed): return Command::ACT;
                    case int(State::Opened):
                        return Command::MAX;
                    default: assert(false);}};
            prereq[int(Level::Bank)][int(Command::WR)] = prereq[int(Level::Bank)][int(Command::RD)];
            prereq[int(Level::SubArray)][int(Command::RD)] = [] (DRAM<SALP>* node, Command cmd, int id) {
              if (node->row_state.find(id) != node->row_state.end()) {
                return cmd;
              } else if (node->row_state.size()) {
                return Command::PRE;
              } else {
                return Command::PRE_OTHER;
              }
            };
            prereq[int(Level::SubArray)][int(Command::WR)] = prereq[int(Level::SubArray)][int(Command::RD)];
            prereq[int(Level::Rank)][int(Command::REF)] = [] (DRAM<SALP>* node, Command cmd, int id) {
                for (auto bank : node->children) {
                    if (bank->state == State::Closed)
                        continue;
                    return Command::PRER;
                }
                return Command::REF;};
            break;
        case int(Type::SALP_2):
            prereq[int(Level::SubArray)][int(Command::RD)] = [] (DRAM<SALP>* node, Command cmd, int id) {
                switch (int(node->state)){
                    case int(State::Closed): return Command::ACT;
                    case int(State::Opened):
                        if (node->row_state.find(id) != node->row_state.end()) {
                          for (auto sa : node->parent->children) {
                              if (sa != node && sa->state == State::Opened) {
                                  return Command::PRE_OTHER;
                              }
                          }
                          return cmd;
                        } else {
                          // if this subarray has another row open, close it
                          // first
                          return Command::PRE;
                        }
                    default: assert(false);}};
            prereq[int(Level::SubArray)][int(Command::WR)] = prereq[int(Level::SubArray)][int(Command::RD)];
            prereq[int(Level::Rank)][int(Command::REF)] = [] (DRAM<SALP>* node, Command cmd, int id) {
                for (auto bank : node->children)
                    for (auto sa : bank->children) {
                        if (sa->state == State::Closed)
                            continue;
                        return Command::PRER;
                    }
                return Command::REF;};
            break;
        case int(Type::MASA):
            prereq[int(Level::SubArray)][int(Command::RD)] = [] (DRAM<SALP>* node, Command cmd, int id) {
                switch (int(node->state)){
                    case int(State::Closed): return Command::ACT;
                    case int(State::Opened):
                        if (node->row_state.find(id) != node->row_state.end()) return Command::SASEL;
                        else return Command::PRE;
                    case int(State::Selected):
                        if (node->row_state.find(id) != node->row_state.end()) return cmd;
                        else return Command::PRE;
                    default: assert(false);
                }};
            prereq[int(Level::SubArray)][int(Command::WR)] = prereq[int(Level::SubArray)][int(Command::RD)];
            prereq[int(Level::Rank)][int(Command::REF)] = [] (DRAM<SALP>* node, Command cmd, int id) {
                for (auto bank : node->children)
                    for (auto sa : bank->children){
                        if (sa->state == State::Closed)
                            continue;
                        return Command::PRER;
                    }
                return Command::REF;};
            break;
        default: assert(false);
    }
    // PD
    prereq[int(Level::Rank)][int(Command::PDE)] = [] (DRAM<SALP>* node, Command cmd, int id) {
        switch (int(node->state)) {
            case int(State::PowerUp): return Command::PDE;
            case int(State::ActPowerDown): return Command::PDE;
            case int(State::PrePowerDown): return Command::PDE;
            case int(State::SelfRefresh): return Command::SRX;
            default: assert(false);
        }};

    // SR
    prereq[int(Level::Rank)][int(Command::SRE)] = [] (DRAM<SALP>* node, Command cmd, int id) {
        switch (int(node->state)) {
            case int(State::PowerUp): return Command::SRE;
            case int(State::ActPowerDown): return Command::PDX;
            case int(State::PrePowerDown): return Command::PDX;
            case int(State::SelfRefresh): return Command::SRE;
            default: assert(false);
        }};
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x30, %rsp
movq %rdi, -0x8(%rbp)
movq -0x8(%rbp), %rdi
movq %rdi, -0x20(%rbp)
addq $0x490, %rdi            # imm = 0x490
movq %rdi, -0x28(%rbp)
leaq -0x9(%rbp), %rsi
callq 0x206760
movq -0x28(%rbp), %rsi
movq -0x20(%rbp), %rdi
addq $0x4b0, %rdi            # imm = 0x4B0
callq 0x2122d0
movq -0x20(%rbp), %rax
movl 0x20(%rax), %eax
movl %eax, -0x18(%rbp)
testl %eax, %eax
je 0x203e8b
jmp 0x203e6c
movl -0x18(%rbp), %eax
subl $0x1, %eax
je 0x203f62
jmp 0x203e7a
movl -0x18(%rbp), %eax
subl $0x2, %eax
je 0x203fe1
jmp 0x20405d
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x380, %rdi            # imm = 0x380
addq $0x80, %rdi
leaq -0xa(%rbp), %rsi
callq 0x2067b0
movq -0x20(%rbp), %rdi
movq %rdi, %rsi
addq $0x250, %rsi            # imm = 0x250
addq $0x380, %rsi            # imm = 0x380
addq $0x80, %rsi
addq $0x250, %rdi            # imm = 0x250
addq $0x380, %rdi            # imm = 0x380
addq $0xa0, %rdi
callq 0x2122d0
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x540, %rdi            # imm = 0x540
addq $0x80, %rdi
leaq -0xb(%rbp), %rsi
callq 0x206800
movq -0x20(%rbp), %rdi
movq %rdi, %rsi
addq $0x250, %rsi            # imm = 0x250
addq $0x540, %rsi            # imm = 0x540
addq $0x80, %rsi
addq $0x250, %rdi            # imm = 0x250
addq $0x540, %rdi            # imm = 0x540
addq $0xa0, %rdi
callq 0x2122d0
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x1c0, %rdi            # imm = 0x1C0
addq $0x100, %rdi            # imm = 0x100
leaq -0xc(%rbp), %rsi
callq 0x206850
jmp 0x20407c
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x540, %rdi            # imm = 0x540
addq $0x80, %rdi
leaq -0xd(%rbp), %rsi
callq 0x2068a0
movq -0x20(%rbp), %rdi
movq %rdi, %rsi
addq $0x250, %rsi            # imm = 0x250
addq $0x540, %rsi            # imm = 0x540
addq $0x80, %rsi
addq $0x250, %rdi            # imm = 0x250
addq $0x540, %rdi            # imm = 0x540
addq $0xa0, %rdi
callq 0x2122d0
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x1c0, %rdi            # imm = 0x1C0
addq $0x100, %rdi            # imm = 0x100
leaq -0xe(%rbp), %rsi
callq 0x2068f0
jmp 0x20407c
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x540, %rdi            # imm = 0x540
addq $0x80, %rdi
leaq -0xf(%rbp), %rsi
callq 0x206940
movq -0x20(%rbp), %rdi
movq %rdi, %rsi
addq $0x250, %rsi            # imm = 0x250
addq $0x540, %rsi            # imm = 0x540
addq $0x80, %rsi
addq $0x250, %rdi            # imm = 0x250
addq $0x540, %rdi            # imm = 0x540
addq $0xa0, %rdi
callq 0x2122d0
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x1c0, %rdi            # imm = 0x1C0
addq $0x100, %rdi            # imm = 0x100
leaq -0x10(%rbp), %rsi
callq 0x206990
jmp 0x20407c
leaq 0x4bafd(%rip), %rdi     # 0x24fb61
leaq 0x5eb6a(%rip), %rsi     # 0x262bd5
movl $0xc8, %edx
leaq 0x5ebbb(%rip), %rcx     # 0x262c32
callq 0xc280
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x1c0, %rdi            # imm = 0x1C0
addq $0x120, %rdi            # imm = 0x120
leaq -0x11(%rbp), %rsi
callq 0x2069e0
movq -0x20(%rbp), %rdi
addq $0x250, %rdi            # imm = 0x250
addq $0x1c0, %rdi            # imm = 0x1C0
addq $0x160, %rdi            # imm = 0x160
leaq -0x12(%rbp), %rsi
callq 0x206a30
addq $0x30, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)