// File: /workspace/github_cpp_makefile_star2_O1/protonpopsicle[P]lang/src/vm/VMachine.cpp
// Function: main
int main(int argc, char *argv[]) {
    if (argv[1] == 0) {
        fprintf(stderr, "missing argument 1: filename\n");
        return(0);
    }
    
    /*
    * Read bytecode
    */
    char *file = argv[1];
    FILE *fp = fopen(file, "r");
    if (fp == 0) {
        fprintf(stderr, "error opening file\n");
        return(0);
    }
    
    int boolCount, intCount, strCount, litCount, opCount;
    
    if (fscanf(fp, "%i %i %i %i %i:", &boolCount, &intCount, &strCount, &litCount, &opCount) != 5) {
        fprintf(stderr, "error reading bytecode");
        return 0;
    }
    
    strCount = strCount + litCount + 1; // string vars + string literals + temp string
    
    char bools[boolCount];
    int ints[intCount];
    std::vector<std::string> strings(strCount);
    Op ops[opCount];
    
    char c;
    for (int i = 0; i < litCount; i++) {
        c = fgetc(fp);
        while (c != ':') {
            strings[i] += c;
            c = fgetc(fp);
        }
    }

    for (int i = 0; i < opCount; i++) {
        fscanf(fp, "%hi %i ", &ops[i].opcode, &ops[i].operand);
    }
    
    fclose(fp);
    
    /*
    * Print debug information
    */
    /*  
    printf("\nBools...\n");
    for (int i = 0; i < boolCount; i++) {
        printf("%i: %i\n", i, bools[i]);
    }
    
    printf("\nInts...\n");
    for (int i = 0; i < intCount; i++) {
        printf("%i: %i\n", i, ints[i]);
    }
    
    printf("\nStrings...\n");
    for (int i = 0; i < strCount; i++) {
        printf("%i: %s\n", i, strings[i].c_str());
    }
    
    printf("\nOpcodes...\n");
    for (int i = 0; i < opCount; i++) {
        std::string opcode;
        
        switch (ops[i].opcode) {
            case OP_PUSH:
                opcode = "OP_PUSH";
                break;
            case OP_GET_BOOL:
                opcode = "OP_GET_BOOL";
                break;
            case OP_GET_INT:
                opcode = "OP_GET_INT";
                break;
            case OP_GET_STRING:
                opcode = "OP_GET_STRING";
                break;
            case OP_ASSIGN_BOOL:
                opcode = "OP_ASSIGN_BOOL";
                break;
            case OP_ASSIGN_INT:
                opcode = "OP_ASSIGN_INT";
                break;
            case OP_ASSIGN_STRING:
                opcode = "OP_ASSIGN_STRING";
                break;
            case OP_ADD:
                opcode = "OP_ADD";
                break;
            case OP_SUBTRACT:
                opcode = "OP_SUBTRACT";
                break;
            case OP_MULTIPLY:
                opcode = "OP_MULTIPLY";
                break;
            case OP_DIVIDE:
                opcode = "OP_DIVIDE";
                break;
            case OP_CAT_STRING:
                opcode = "OP_CAT_STRING";
                break;
            case OP_CAT_STRING_INT:
                opcode = "OP_CAT_STRING_INT";
                break;
            case OP_CAT_INT_STRING:
                opcode = "OP_CAT_INT_STRING";
                break;
            case OP_PRINT_INT:
                opcode = "OP_PRINT_INT";
                break;
            case OP_PRINT_STRING:
                opcode = "OP_PRINT_STRING";
                break;
            case OP_JUMP:
                opcode = "OP_JUMP";
                break;
            case OP_JUMP_IF_FALSE:
                opcode = "OP_JUMP_IF_FALSE";
                break;
            case OP_L:
                opcode = "OP_L";
                break;
            case OP_G:
                opcode = "OP_G";
                break;
            case OP_LE:
                opcode = "OP_LE";
                break;
            case OP_GE:
                opcode = "OP_GE";
                break;
            case OP_EQ:
                opcode = "OP_EQ";
                break;
            case OP_NE:
                opcode = "OP_NE";
                break;
            case OP_AND:
                opcode = "OP_AND";
                break;
            case OP_OR:
                opcode = "OP_OR";
                break;
            default:
                opcode = "undefined";
        }
        
        printf("%i: %s %i\n", i, opcode.c_str(), ops[i].operand);
    }
    printf("\n");
    */  
    /*
    * Run program
    */
    bool a, b;
    int x, y;
    std::string strA, strB;
    char intBuffer[12];
    Stack stack(2);
    
    for (int i = 0; i < opCount; i++) {
        switch (ops[i].opcode) {
            case OP_PUSH:
                stack.push(i, ops[i].operand);
                break;
            case OP_GET_BOOL:
                stack.push(i, bools[ops[i].operand]);
                break;
            case OP_GET_INT:
                stack.push(i,ints[ops[i].operand]);
                break;
            case OP_GET_STRING:
                // offset by litCount because variables come after literals in array
                stack.push(i, ops[i].operand + litCount);
                break;
            case OP_ASSIGN_BOOL:
                bools[ops[i].operand] = stack.pop();
                break;
            case OP_ASSIGN_INT:
                ints[ops[i].operand] = stack.pop();
                break;
            case OP_ASSIGN_STRING:
                strings[ops[i].operand + litCount] = strings[stack.pop()];
                break;
            case OP_ADD:
                y = stack.pop(); 
                x = stack.pop();
                stack.push(i, x + y);
                break;
            case OP_SUBTRACT:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x - y);
                break;
            case OP_MULTIPLY:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x * y);
                break;
            case OP_DIVIDE:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x / y);
                break;
            case OP_CAT_STRING:
                strB = strings[stack.pop()];
                strA = strings[stack.pop()];
                strings[strCount - 1] = strA + strB;
                stack.push(i, strCount - 1);
                break;
            case OP_CAT_STRING_INT:
                x = stack.pop();
                strA = strings[stack.pop()];
                snprintf(intBuffer, sizeof(intBuffer), "%i", x);
                strings[strCount - 1] = strA.append(intBuffer);
                stack.push(i, strCount - 1);
                break;
            case OP_CAT_INT_STRING:
                strA = strings[stack.pop()];
                x = stack.pop();
                snprintf(intBuffer, sizeof(intBuffer), "%i", x);
                strings[strCount - 1] = strA.insert(0, intBuffer);
                stack.push(i, strCount - 1);
                break;
            case OP_PRINT_INT:
                x = stack.pop();
                printf("%i", x);
                break;
            case OP_JUMP:
                i += ops[i].operand - 1;
                break;
            case OP_JUMP_IF_FALSE:
                if (!stack.pop()) {
                    i += ops[i].operand;
                }
                break;
            case OP_PRINT_STRING:
                strA = strings[stack.pop()];
                printf("%s", strA.c_str());
                break;
            case OP_L:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x < y);
                break;
            case OP_G:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x > y);
                break;
            case OP_LE:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x <= y);
                break;
            case OP_GE:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x >= y);
                break;
            case OP_EQ:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x == y);
                break;
            case OP_NE:
                y = stack.pop();
                x = stack.pop();
                stack.push(i, x != y);
                break;
            case OP_AND:
                b = stack.pop();
                a = stack.pop();
                stack.push(i, a && b);
                break;
            case OP_OR:
                b = stack.pop();
                a = stack.pop();
                stack.push(i, b || a);
                break;
        }
    }
    
    return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xc8, %rsp
movq 0x8(%rsi), %rdi
testq %rdi, %rdi
je 0x1a2d
leaq 0xcde(%rip), %rsi       # 0x2092
callq 0x1120
testq %rax, %rax
je 0x1a34
movq %rax, %rbx
subq $0x8, %rsp
leaq -0x40(%rbp), %r10
leaq 0xcd4(%rip), %rsi       # 0x20a8
leaq -0x68(%rbp), %rdx
leaq -0x64(%rbp), %rcx
leaq -0x3c(%rbp), %r8
leaq -0x44(%rbp), %r9
movq %rax, %rdi
xorl %eax, %eax
pushq %r10
callq 0x1190
addq $0x10, %rsp
cmpl $0x5, %eax
jne 0x1a3b
movq %rsp, %r12
movl -0x3c(%rbp), %eax
movl -0x44(%rbp), %ecx
addl %ecx, %eax
incl %eax
movl %eax, -0x3c(%rbp)
movl -0x68(%rbp), %eax
movq %rsp, %rcx
addq $0xf, %rax
andq $-0x10, %rax
subq %rax, %rcx
movq %rcx, -0x98(%rbp)
movq %rcx, %rsp
movl -0x64(%rbp), %eax
movq %rsp, %rcx
leaq 0xf(,%rax,4), %rax
andq $-0x10, %rax
subq %rax, %rcx
movq %rcx, -0x90(%rbp)
movq %rcx, %rsp
movslq -0x3c(%rbp), %rsi
leaq -0x60(%rbp), %rdi
leaq -0x88(%rbp), %rdx
callq 0x1aa0
movl -0x40(%rbp), %eax
movq %rsp, %r13
leaq 0xf(,%rax,8), %rax
andq $-0x10, %rax
subq %rax, %r13
movq %r13, %rsp
cmpl $0x0, -0x44(%rbp)
jle 0x14b6
xorl %r14d, %r14d
movq %rbx, %rdi
callq 0x1150
cmpb $0x3a, %al
je 0x14aa
movq %r14, %r15
shlq $0x5, %r15
movq -0x60(%rbp), %rdi
addq %r15, %rdi
movsbl %al, %esi
callq 0x10d0
movq %rbx, %rdi
callq 0x1150
cmpb $0x3a, %al
jne 0x148f
incq %r14
movslq -0x44(%rbp), %rax
cmpq %rax, %r14
jl 0x147c
movq %r12, -0xa0(%rbp)
cmpl $0x0, -0x40(%rbp)
jle 0x14f4
leaq 0xc05(%rip), %r14       # 0x20cf
movq %r13, %r15
xorl %r12d, %r12d
leaq 0x4(%r15), %rcx
movq %rbx, %rdi
movq %r14, %rsi
movq %r15, %rdx
xorl %eax, %eax
callq 0x1190
incq %r12
movslq -0x40(%rbp), %rax
addq $0x8, %r15
cmpq %rax, %r12
jl 0x14d0
movq %rbx, %rdi
callq 0x10e0
leaq -0x78(%rbp), %r15
movq %r15, -0x10(%r15)
xorl %eax, %eax
movq %rax, -0x8(%r15)
movb %al, (%r15)
leaq -0xc0(%rbp), %r12
movq %r12, -0x10(%r12)
movq %rax, -0x8(%r12)
movb %al, (%r12)
leaq -0x38(%rbp), %rdi
movl $0x2, %esi
callq 0x132c
cmpl $0x0, -0x40(%rbp)
jle 0x19cd
xorl %ebx, %ebx
movslq %ebx, %r14
movzwl (%r13,%r14,8), %eax
cmpq $0x1b, %rax
ja 0x19c2
leaq 0xaae(%rip), %rcx       # 0x2004
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl 0x4(%r13,%r14,8), %edx
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
xorl %edx, %edx
cmpl %r14d, %eax
setl %dl
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
xorl %edx, %edx
cmpl %r14d, %eax
setge %dl
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x88(%rbp), %rdi
callq 0x1090
leaq -0x38(%rbp), %rdi
callq 0x136e
leaq 0xafa(%rip), %rdx       # 0x20d7
leaq -0xac(%rbp), %r14
movl $0xc, %esi
movq %r14, %rdi
movl %eax, %ecx
xorl %eax, %eax
callq 0x1160
movq %r14, %rdi
callq 0x1070
leaq -0x88(%rbp), %rdi
xorl %esi, %esi
xorl %edx, %edx
movq %r14, %rcx
movq %rax, %r8
callq 0x1110
movslq -0x3c(%rbp), %rcx
movq -0x60(%rbp), %rdx
shlq $0x5, %rcx
leaq (%rdx,%rcx), %rdi
addq $-0x20, %rdi
movq %rax, %rsi
callq 0x1090
jmp 0x183c
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
xorl %edx, %edx
orl %r14d, %eax
jmp 0x166a
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
xorl %edx, %edx
cmpl %r14d, %eax
setne %dl
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
cltd
idivl %r14d
movl %eax, %edx
jmp 0x19b7
movslq 0x4(%r13,%r14,8), %rax
movq -0x90(%rbp), %rcx
movl (%rcx,%rax,4), %edx
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq 0x4(%r13,%r14,8), %rcx
movq -0x98(%rbp), %rdx
movb %al, (%rdx,%rcx)
jmp 0x19c2
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
xorl %edx, %edx
cmpl %r14d, %eax
sete %dl
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x88(%rbp), %rdi
callq 0x1090
leaq 0x9bf(%rip), %rdx       # 0x20d7
leaq -0xac(%rbp), %rdi
movl $0xc, %esi
movl %r14d, %ecx
xorl %eax, %eax
callq 0x1160
leaq -0x88(%rbp), %rdi
leaq -0xac(%rbp), %rsi
callq 0x11a0
movslq -0x3c(%rbp), %rcx
movq -0x60(%rbp), %rdx
shlq $0x5, %rcx
leaq (%rdx,%rcx), %rdi
addq $-0x20, %rdi
movq %rax, %rsi
callq 0x1090
jmp 0x183c
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
xorl %edx, %edx
cmpl %r14d, %eax
setg %dl
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq 0x4(%r13,%r14,8), %rcx
movq -0x90(%rbp), %rdx
movl %eax, (%rdx,%rcx,4)
jmp 0x19c2
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0xd0(%rbp), %rdi
callq 0x1090
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x88(%rbp), %rdi
callq 0x1090
leaq -0xf0(%rbp), %rdi
leaq -0x88(%rbp), %rsi
leaq -0xd0(%rbp), %rdx
callq 0x1b29
movslq -0x3c(%rbp), %rax
movq -0x60(%rbp), %rcx
shlq $0x5, %rax
leaq (%rcx,%rax), %rdi
addq $-0x20, %rdi
leaq -0xf0(%rbp), %r14
movq %r14, %rsi
callq 0x1130
movq (%r14), %rdi
leaq -0xe0(%rbp), %rax
cmpq %rax, %rdi
je 0x183c
movq -0xe0(%rbp), %rsi
incq %rsi
callq 0x1100
movl -0x3c(%rbp), %edx
decl %edx
jmp 0x19b7
movl -0x44(%rbp), %edx
addl 0x4(%r13,%r14,8), %edx
jmp 0x19b7
movslq 0x4(%r13,%r14,8), %rax
movq -0x98(%rbp), %rcx
movsbl (%rcx,%rax), %edx
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
testl %r14d, %r14d
setne %cl
testl %eax, %eax
setne %al
andb %cl, %al
movzbl %al, %edx
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq %eax, %rsi
movq -0x60(%rbp), %rax
shlq $0x5, %rsi
addq %rax, %rsi
movslq 0x4(%r13,%r14,8), %rcx
movslq -0x44(%rbp), %rdi
addq %rcx, %rdi
shlq $0x5, %rdi
addq %rax, %rdi
callq 0x1090
jmp 0x19c2
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %edx
addl %r14d, %edx
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
leaq 0x7e2(%rip), %rdi       # 0x20d7
movl %eax, %esi
xorl %eax, %eax
callq 0x1040
jmp 0x19c2
leaq -0x38(%rbp), %rdi
callq 0x136e
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x88(%rbp), %rdi
callq 0x1090
movq -0x88(%rbp), %rsi
leaq 0x7a9(%rip), %rdi       # 0x20da
xorl %eax, %eax
callq 0x1040
jmp 0x19c2
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %edx
imull %r14d, %edx
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %edx
subl %r14d, %edx
jmp 0x19b7
leaq -0x38(%rbp), %rdi
callq 0x136e
testl %eax, %eax
jne 0x19c2
movl 0x4(%r13,%r14,8), %ebx
addl %r14d, %ebx
jmp 0x19c2
movl 0x4(%r13,%r14,8), %eax
leal (%r14,%rax), %ebx
decl %ebx
jmp 0x19c2
leaq -0x38(%rbp), %rdi
callq 0x136e
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x136e
xorl %edx, %edx
cmpl %r14d, %eax
setle %dl
leaq -0x38(%rbp), %rdi
movl %ebx, %esi
callq 0x135c
incl %ebx
cmpl -0x40(%rbp), %ebx
jl 0x153c
leaq -0x38(%rbp), %rdi
callq 0x137e
movq -0xd0(%rbp), %rdi
cmpq %r12, %rdi
je 0x19f1
movq -0xc0(%rbp), %rsi
incq %rsi
callq 0x1100
movq -0x88(%rbp), %rdi
cmpq %r15, %rdi
movq -0xa0(%rbp), %rbx
je 0x1a10
movq -0x78(%rbp), %rsi
incq %rsi
callq 0x1100
leaq -0x60(%rbp), %rdi
callq 0x1b84
movq %rbx, %rsp
xorl %eax, %eax
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x120e
jmp 0x1a1c
callq 0x11f2
jmp 0x1a1c
callq 0x11d0
jmp 0x1a1c
jmp 0x1a4b
movq %rax, %rbx
jmp 0x1a57
jmp 0x1a8c
movq %rax, %rbx
leaq -0x38(%rbp), %rdi
callq 0x137e
movq -0xd0(%rbp), %rdi
cmpq %r12, %rdi
je 0x1a72
movq -0xc0(%rbp), %rsi
incq %rsi
callq 0x1100
movq -0x88(%rbp), %rdi
cmpq %r15, %rdi
je 0x1a8f
movq -0x78(%rbp), %rsi
incq %rsi
callq 0x1100
jmp 0x1a8f
movq %rax, %rbx
leaq -0x60(%rbp), %rdi
callq 0x1b84
movq %rbx, %rdi
callq 0x1170