// File: /workspace/github_c_makefile_star2_O3/tstword[P]MIPSAssemblerC/src/assembler.c
// Function: assemble_opcode_instruction
int assemble_opcode_instruction(struct instruction_node *instr) {
    struct opcode_entry *entry = (struct opcode_entry *)instr->mnemonic->attrptr;
    
    int assemble_status = 1; /* Assume instruction is assembled */

    switch(entry - opcode_table) {
        case MNEMONIC_ADDI:
        case MNEMONIC_ADDIU:
        case MNEMONIC_SLTI:
        case MNEMONIC_SLTIU: { /* ALU OP */
            struct operand_node *rt = instr->operand_list;
            struct operand_node *rs = instr->operand_list->next;
            struct operand_node *imm = instr->operand_list->next->next;
            int immediate = imm->value.integer; /* Temporary fix, still figuring out how to handle this generally */

            if(((immediate >> 15) & 0x1FFFF) != 0x1FFFF && ((immediate >> 15) & 0x1FFFF) != 0x00000) {
                /* 32-bit sign extended or unsigned value */
                write_instruction(CREATE_INSTRUCTION_I(0x0F, 0, 1, (immediate >> 16)));
                write_instruction(CREATE_INSTRUCTION_I(0x0D, 1, 1, immediate));
                write_instruction(CREATE_INSTRUCTION_R(0, rs->value.reg, 1, rt->value.reg, 0, entry->opcode + 0x18));
            }
            else {
                write_instruction(CREATE_INSTRUCTION_I(entry->opcode, rs->value.reg, rt->value.reg, imm->value.integer));
            }
            break;
        }

        case MNEMONIC_ANDI:
        case MNEMONIC_ORI:
        case MNEMONIC_XORI: { /* ALU OP */
            struct operand_node *rt = instr->operand_list;
            struct operand_node *rs = instr->operand_list->next;
            struct operand_node *imm = instr->operand_list->next->next;
            int immediate = imm->value.integer; /* Temporary fix, still figuring out how to handle this generally */

            if(((immediate >> 16) & 0xFFFF) != 0x0000) {
                /* 32-bit sign extended or unsigned value */
                write_instruction(CREATE_INSTRUCTION_I(0x0F, 0, 1, (immediate >> 16)));
                write_instruction(CREATE_INSTRUCTION_I(0x0D, 1, 1, immediate));
                write_instruction(CREATE_INSTRUCTION_R(0, rs->value.reg, 1, rt->value.reg, 0, entry->opcode + 0x18));
            }
            else {
                write_instruction(CREATE_INSTRUCTION_I(entry->opcode, rs->value.reg, rt->value.reg, imm->value.integer));
            }
            break;
        }

        case MNEMONIC_LUI: {
            struct operand_node *rd = instr->operand_list;
            struct operand_node *imm = instr->operand_list->next;
            uint32_t immediate = imm->value.integer;
            write_instruction(CREATE_INSTRUCTION_I(entry->opcode, 0, rd->value.reg, immediate));
            break;
        }

        case MNEMONIC_BGEZ:
        case MNEMONIC_BGEZAL:
        case MNEMONIC_BLTZ:
        case MNEMONIC_BLTZAL:
        case MNEMONIC_BGTZ:
        case MNEMONIC_BLEZ: {
            struct operand_node *rs = instr->operand_list;
            struct operand_node *label = instr->operand_list->next;
            /* Check if label has been defined */
            struct symbol_table_entry *sym_entry = get_symbol_table(cfg_assembler->symbol_table, label->identifier);
            if(sym_entry->status == SYMBOL_UNDEFINED) {
                insert_front(sym_entry->instr_list, (void *)instr);
                assemble_status = 0;
            }
            else {
                write_instruction(CREATE_INSTRUCTION_I(entry->opcode, rs->value.reg, entry->rt, get_branch_offset(sym_entry)));
            }   
            break;
        }

        case MNEMONIC_BNE:
        case MNEMONIC_BEQ: {
            struct operand_node *rs = instr->operand_list;
            struct operand_node *rt = instr->operand_list->next;
            struct operand_node *label = instr->operand_list->next->next;
            int rt_imm = rt->operand & OPERAND_IMMEDIATE;

            /* TO-DO: Determine a way to distinguish between positive and negative integers (32-bit) */

            /* Check if label has been defined */
            struct symbol_table_entry *sym_entry = get_symbol_table(cfg_assembler->symbol_table, label->identifier);
            if(sym_entry->status == SYMBOL_UNDEFINED) {
                assemble_status = 0;
                if(rt_imm) incr_segment_offset(0x4); /* Special Case: Immediate operand requies extra instruction */
                insert_front(sym_entry->instr_list, (void *)instr);
            }
            else {
                if(rt_imm) {
                    write_instruction(CREATE_INSTRUCTION_I(0x08, 0, 1, rt->value.integer));
                    write_instruction(CREATE_INSTRUCTION_I(entry->opcode, 1, rs->value.reg, get_branch_offset(sym_entry)));
                }
                else {
                    write_instruction(CREATE_INSTRUCTION_I(entry->opcode, rs->value.reg, rt->value.reg, get_branch_offset(sym_entry)));
                }
            }
            break;
        }

        case MNEMONIC_JMP:
        case MNEMONIC_JAL: {
            struct operand_node *label = instr->operand_list;
            /* Check if label has been defined */
            struct symbol_table_entry *sym_entry = get_symbol_table(cfg_assembler->symbol_table, label->identifier);
            if(sym_entry->status == SYMBOL_UNDEFINED) {
                insert_front(sym_entry->instr_list, (void *)instr);
                assemble_status = 0;
            }
            else {
                write_instruction(CREATE_INSTRUCTION_J(entry->opcode, (sym_entry->offset >> 2)));   
            }
            break;
        }
        

        case MNEMONIC_LB:
        case MNEMONIC_LBU:
        case MNEMONIC_LH:
        case MNEMONIC_LHU:
        case MNEMONIC_LW: 
        case MNEMONIC_SB: 
        case MNEMONIC_SH:
        case MNEMONIC_SW: {
            struct operand_node *rt = instr->operand_list;
            struct operand_node *addr = instr->operand_list->next;
            if(addr->operand == OPERAND_LABEL) {
                struct symbol_table_entry *sym_entry = get_symbol_table(cfg_assembler->symbol_table, addr->identifier);
                if(sym_entry->status == SYMBOL_UNDEFINED) {
                    insert_front(sym_entry->instr_list, (void *)instr);
                    incr_segment_offset(0x4); /* Special Case: Psuedo instruction requires 8 bytes */
                    assemble_status = 0;
                }
                else {
                    write_instruction(CREATE_INSTRUCTION_I(0x0F, 0, 1, (sym_entry->offset >> 16)));
                    write_instruction(CREATE_INSTRUCTION_I(entry->opcode, 1, rt->value.reg, sym_entry->offset));
                }
            } 
            else {
                write_instruction(CREATE_INSTRUCTION_I(entry->opcode, addr->value.reg, rt->value.reg, addr->value.integer));
            }
            break;
        }

        case MNEMONIC_MUL: {
            struct operand_node *rd = instr->operand_list;
            struct operand_node *rs = instr->operand_list->next;
            struct operand_node *rt = instr->operand_list->next->next;
            write_instruction(CREATE_INSTRUCTION_R(entry->opcode, rs->value.reg, rt->value.reg, rd->value.reg, 0, entry->funct));
            break;
        }
    }

    if(!assemble_status) incr_segment_offset(0x4);

    return assemble_status;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
pushq %rax
movq (%rdi), %rax
movq 0x10(%rax), %r12
movq %r12, %rax
subq 0x6379(%rip), %rax      # 0xafb8
sarq $0x3, %rax
movl $0x1, %ebx
addq $-0xd, %rax
cmpq $0x38, %rax
ja 0x4f80
movq %rdi, %r14
leaq 0x2504(%rip), %rcx      # 0x7164
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq 0x8(%r14), %r13
movq 0x10(%r13), %rax
cmpb $0x1, (%rax)
jne 0x4d82
movq 0x7937(%rip), %rcx      # 0xc5b8
movq 0x10(%rcx), %rdi
movq 0x8(%rax), %rsi
callq 0x616b
movq %rax, %r15
cmpb $0x0, 0x8(%rax)
je 0x4ef9
movzwl 0xe(%r15), %edi
orl $0x3c010000, %edi       # imm = 0x3C010000
callq 0x399b
movzbl (%r12), %eax
shll $0x1a, %eax
movzbl 0xd(%r13), %ecx
andl $0x1f, %ecx
shll $0x10, %ecx
orl %eax, %ecx
movzwl 0xc(%r15), %eax
leal (%rax,%rcx), %edi
addl $0x200000, %edi         # imm = 0x200000
jmp 0x4f7b
movq 0x8(%r14), %r15
movq 0x10(%r15), %rax
movq 0x78d6(%rip), %rcx      # 0xc5b8
movq 0x10(%rcx), %rdi
movq 0x8(%rax), %rsi
callq 0x616b
cmpb $0x0, 0x8(%rax)
je 0x4fa7
movzbl (%r12), %ecx
shll $0x1a, %ecx
movzbl 0xd(%r15), %edx
andl $0x1f, %edx
shll $0x15, %edx
orl %ecx, %edx
movzbl 0x2(%r12), %ecx
andl $0x1f, %ecx
shll $0x10, %ecx
orl %edx, %ecx
movl 0xc(%rax), %eax
movq 0x7892(%rip), %rdx      # 0xc5b8
movzbl 0x45(%rdx), %esi
subl 0x48(%rdx,%rsi,4), %eax
addl $0x3fffc, %eax          # imm = 0x3FFFC
shrl $0x2, %eax
movzwl %ax, %edi
jmp 0x4f79
movq 0x8(%r14), %r14
movq 0x10(%r14), %r15
movq 0x10(%r15), %rax
movl 0x8(%rax), %ebp
movswl %bp, %eax
cmpl %ebp, %eax
jne 0x4dec
movzbl (%r12), %eax
shll $0x1a, %eax
movzbl 0xd(%r15), %ecx
andl $0x1f, %ecx
shll $0x15, %ecx
orl %eax, %ecx
movzbl 0xd(%r14), %eax
andl $0x1f, %eax
shll $0x10, %eax
orl %ecx, %eax
movzwl %bp, %edi
jmp 0x4f55
movzbl (%r12), %ecx
shll $0x1a, %ecx
movzbl 0xd(%rax), %edx
andl $0x1f, %edx
shll $0x15, %edx
orl %ecx, %edx
movzbl 0xd(%r13), %ecx
andl $0x1f, %ecx
shll $0x10, %ecx
orl %edx, %ecx
movzwl 0x8(%rax), %edi
jmp 0x4f79
movq 0x8(%r14), %r14
movq 0x10(%r14), %r15
movq 0x10(%r15), %rax
movl 0x8(%rax), %ebp
cmpl $0x10000, %ebp          # imm = 0x10000
jae 0x4dec
movzbl (%r12), %eax
shll $0x1a, %eax
movzbl 0xd(%r15), %ecx
andl $0x1f, %ecx
shll $0x15, %ecx
orl %eax, %ecx
movzbl 0xd(%r14), %edi
andl $0x1f, %edi
shll $0x10, %edi
orl %ecx, %edi
orl %ebp, %edi
jmp 0x4f7b
movl %ebp, %edi
shrl $0x10, %edi
orl $0x3c010000, %edi       # imm = 0x3C010000
callq 0x399b
movzwl %bp, %edi
orl $0x34210000, %edi       # imm = 0x34210000
callq 0x399b
movzbl 0xd(%r15), %eax
andl $0x1f, %eax
shll $0x15, %eax
movzbl 0xd(%r14), %ecx
andl $0x1f, %ecx
shll $0xb, %ecx
orl %eax, %ecx
movb (%r12), %al
addb $0x18, %al
andb $0x3f, %al
movzbl %al, %eax
leal (%rax,%rcx), %edi
addl $0x10000, %edi          # imm = 0x10000
jmp 0x4f7b
movq 0x8(%r14), %r13
movq 0x10(%r13), %rbp
movq 0x10(%rbp), %rax
movb (%rbp), %r15b
movq 0x7766(%rip), %rcx      # 0xc5b8
movq 0x10(%rcx), %rdi
movq 0x8(%rax), %rsi
callq 0x616b
cmpb $0x0, 0x8(%rax)
je 0x4f91
testb $0x2, %r15b
jne 0x4fc1
movzbl (%r12), %edx
shll $0x1a, %edx
movzbl 0xd(%r13), %ecx
andl $0x1f, %ecx
shll $0x15, %ecx
orl %edx, %ecx
movzbl 0xd(%rbp), %r8d
andl $0x1f, %r8d
shll $0x10, %r8d
orl %ecx, %r8d
movl 0xc(%rax), %ecx
movq 0x7716(%rip), %rdx      # 0xc5b8
movzbl 0x45(%rdx), %esi
subl 0x48(%rdx,%rsi,4), %ecx
addl $0x3fffc, %ecx          # imm = 0x3FFFC
shrl $0x2, %ecx
movzwl %cx, %edi
orl %r8d, %edi
jmp 0x4f7b
movq 0x8(%r14), %rax
movq 0x76ef(%rip), %rcx      # 0xc5b8
movq 0x10(%rcx), %rdi
movq 0x8(%rax), %rsi
callq 0x616b
cmpb $0x0, 0x8(%rax)
je 0x4fa7
movzbl (%r12), %ecx
shll $0x1a, %ecx
movl 0xc(%rax), %edi
shrl $0x2, %edi
andl $0x3ffffff, %edi        # imm = 0x3FFFFFF
jmp 0x4f79
movq 0x18(%r15), %rdi
movq %r14, %rsi
callq 0x5963
movl $0x4, %edi
callq 0x37dc
jmp 0x4fb3
movq 0x8(%r14), %rax
movq 0x10(%rax), %rcx
movq 0x10(%rcx), %rdx
movzbl (%r12), %esi
shll $0x1a, %esi
movzbl 0xd(%rcx), %ecx
andl $0x1f, %ecx
shll $0x15, %ecx
orl %esi, %ecx
movzbl 0xd(%rdx), %edx
andl $0x1f, %edx
shll $0x10, %edx
orl %ecx, %edx
movzbl 0xd(%rax), %eax
andl $0x1f, %eax
shll $0xb, %eax
orl %edx, %eax
movzbl 0x1(%r12), %edi
andl $0x3f, %edi
orl %eax, %edi
jmp 0x4f7b
movq 0x8(%r14), %rax
movq 0x10(%rax), %rcx
movzwl 0x8(%rcx), %ecx
movzbl (%r12), %edx
shll $0x1a, %edx
movzbl 0xd(%rax), %edi
andl $0x1f, %edi
shll $0x10, %edi
orl %edx, %edi
orl %ecx, %edi
callq 0x399b
movl %ebx, %eax
addq $0x8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
testb $0x2, %r15b
je 0x4fa7
movl $0x4, %edi
movq %rax, %rbx
callq 0x37dc
movq %rbx, %rax
movq 0x18(%rax), %rdi
movq %r14, %rsi
callq 0x5963
movl $0x4, %edi
callq 0x37dc
xorl %ebx, %ebx
jmp 0x4f80
movzwl 0x8(%rbp), %edi
orl $0x20010000, %edi       # imm = 0x20010000
movq %rax, %r14
callq 0x399b
movzbl (%r12), %eax
shll $0x1a, %eax
movzbl 0xd(%r13), %ecx
andl $0x1f, %ecx
shll $0x10, %ecx
orl %eax, %ecx
movl 0xc(%r14), %eax
movq 0x75c5(%rip), %rdx      # 0xc5b8
movzbl 0x45(%rdx), %esi
subl 0x48(%rdx,%rsi,4), %eax
addl $0x3fffc, %eax          # imm = 0x3FFFC
shrl $0x2, %eax
movzwl %ax, %eax
jmp 0x4cc5