// File: /workspace/github_cpp_makefile_star2_O2/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 0x1934
leaq 0xd5a(%rip), %rsi       # 0x2092
callq 0x1110
testq %rax, %rax
je 0x1949
movq %rax, %rbx
subq $0x8, %rsp
leaq -0x44(%rbp), %r10
leaq 0xd50(%rip), %rsi       # 0x20a8
leaq -0x68(%rbp), %rdx
leaq -0x64(%rbp), %rcx
leaq -0x3c(%rbp), %r8
leaq -0x40(%rbp), %r9
movq %rax, %rdi
xorl %eax, %eax
pushq %r10
callq 0x1180
popq %rcx
popq %rdx
cmpl $0x5, %eax
jne 0x195e
movq %rsp, %r15
movl -0x3c(%rbp), %eax
movl -0x40(%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, -0x78(%rbp)
movq %rcx, %rsp
movl -0x64(%rbp), %eax
movq %rsp, %rcx
leaq 0xf(,%rax,4), %rax
andq $-0x10, %rax
subq %rax, %rcx
movq %rcx, -0x70(%rbp)
movq %rcx, %rsp
movslq -0x3c(%rbp), %rsi
leaq -0x60(%rbp), %rdi
leaq -0x98(%rbp), %rdx
callq 0x19c0
movl -0x44(%rbp), %eax
movq %rsp, %r13
leaq 0xf(,%rax,8), %rax
andq $-0x10, %rax
subq %rax, %r13
movq %r13, %rsp
xorl %r14d, %r14d
movslq -0x40(%rbp), %rax
cmpq %rax, %r14
jge 0x1421
movq %rbx, %rdi
callq 0x1150
cmpb $0x3a, %al
je 0x141c
movq %r14, %rdi
shlq $0x5, %rdi
addq -0x60(%rbp), %rdi
movsbl %al, %esi
callq 0x10d0
jmp 0x13fb
incq %r14
jmp 0x13f2
movq %r15, -0xa0(%rbp)
leaq 0xca0(%rip), %r14       # 0x20cf
movq %r13, %r15
xorl %r12d, %r12d
movslq -0x44(%rbp), %rax
cmpq %rax, %r12
jge 0x145b
leaq 0x4(%r15), %rcx
movq %rbx, %rdi
movq %r14, %rsi
movq %r15, %rdx
xorl %eax, %eax
callq 0x1180
incq %r12
addq $0x8, %r15
jmp 0x1435
movq %rbx, %rdi
callq 0x10e0
leaq -0x88(%rbp), %rax
movq %rax, -0x10(%rax)
andq $0x0, -0x8(%rax)
xorl %ebx, %ebx
movb %bl, (%rax)
leaq -0xc0(%rbp), %rax
movq %rax, -0x10(%rax)
andq $0x0, -0x8(%rax)
movb %bl, (%rax)
leaq -0x38(%rbp), %rdi
pushq $0x2
popq %rsi
callq 0x12bc
leaq 0xb68(%rip), %r12       # 0x2004
cmpl -0x44(%rbp), %ebx
jge 0x18f2
movslq %ebx, %r14
movzwl (%r13,%r14,8), %eax
cmpq $0x1b, %rax
ja 0x18eb
movslq (%r12,%rax,4), %rax
addq %r12, %rax
jmpq *%rax
movl 0x4(%r13,%r14,8), %edx
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
xorl %edx, %edx
cmpl %r14d, %eax
setl %dl
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
xorl %edx, %edx
cmpl %r14d, %eax
setge %dl
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x98(%rbp), %rdi
callq 0x10b0
leaq -0x38(%rbp), %rdi
callq 0x12f2
leaq 0xb98(%rip), %rdx       # 0x20d7
leaq -0xac(%rbp), %r14
pushq $0xc
popq %rsi
movq %r14, %rdi
movl %eax, %ecx
xorl %eax, %eax
callq 0x1160
leaq -0x98(%rbp), %rdi
xorl %esi, %esi
movq %r14, %rdx
callq 0x1050
movslq -0x3c(%rbp), %rcx
movq -0x60(%rbp), %rdx
shlq $0x5, %rcx
leaq (%rdx,%rcx), %rdi
addq $-0x20, %rdi
movq %rax, %rsi
callq 0x10b0
jmp 0x176d
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
xorl %edx, %edx
orl %r14d, %eax
jmp 0x15bd
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
xorl %edx, %edx
cmpl %r14d, %eax
setne %dl
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
cltd
idivl %r14d
movl %eax, %edx
jmp 0x18e0
movslq 0x4(%r13,%r14,8), %rax
movq -0x70(%rbp), %rcx
movl (%rcx,%rax,4), %edx
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq 0x4(%r13,%r14,8), %rcx
movq -0x78(%rbp), %rdx
movb %al, (%rdx,%rcx)
jmp 0x18eb
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
xorl %edx, %edx
cmpl %r14d, %eax
sete %dl
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x98(%rbp), %rdi
callq 0x10b0
leaq 0xa72(%rip), %rdx       # 0x20d7
leaq -0xac(%rbp), %r15
pushq $0xc
popq %rsi
movq %r15, %rdi
movl %r14d, %ecx
xorl %eax, %eax
callq 0x1160
leaq -0x98(%rbp), %rdi
movq %r15, %rsi
callq 0x1190
movslq -0x3c(%rbp), %rcx
movq -0x60(%rbp), %rdx
shlq $0x5, %rcx
leaq (%rdx,%rcx), %rdi
addq $-0x20, %rdi
movq %rax, %rsi
callq 0x10b0
jmp 0x176d
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
xorl %edx, %edx
cmpl %r14d, %eax
setg %dl
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq 0x4(%r13,%r14,8), %rcx
movq -0x70(%rbp), %rdx
movl %eax, (%rdx,%rcx,4)
jmp 0x18eb
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0xd0(%rbp), %rdi
callq 0x10b0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x98(%rbp), %rdi
callq 0x10b0
leaq -0xf0(%rbp), %rdi
leaq -0x98(%rbp), %rsi
leaq -0xd0(%rbp), %rdx
callq 0x1a11
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 0x1120
movq %r14, %rdi
callq 0x1090
movl -0x3c(%rbp), %edx
decl %edx
jmp 0x18e0
movl -0x40(%rbp), %edx
addl 0x4(%r13,%r14,8), %edx
jmp 0x18e0
movslq 0x4(%r13,%r14,8), %rax
movq -0x78(%rbp), %rcx
movsbl (%rcx,%rax), %edx
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
testl %r14d, %r14d
setne %cl
testl %eax, %eax
setne %al
andb %cl, %al
movzbl %al, %edx
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq %eax, %rsi
movq -0x60(%rbp), %rax
shlq $0x5, %rsi
addq %rax, %rsi
movslq 0x4(%r13,%r14,8), %rcx
movslq -0x40(%rbp), %rdi
addq %rcx, %rdi
shlq $0x5, %rdi
addq %rax, %rdi
callq 0x10b0
jmp 0x18eb
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %edx
addl %r14d, %edx
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
leaq 0x8b4(%rip), %rdi       # 0x20d7
movl %eax, %esi
xorl %eax, %eax
callq 0x1040
jmp 0x18eb
leaq -0x38(%rbp), %rdi
callq 0x12f2
movslq %eax, %rsi
shlq $0x5, %rsi
addq -0x60(%rbp), %rsi
leaq -0x98(%rbp), %rdi
callq 0x10b0
movq -0x98(%rbp), %rsi
leaq 0x87b(%rip), %rdi       # 0x20da
xorl %eax, %eax
callq 0x1040
jmp 0x18eb
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %edx
imull %r14d, %edx
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %edx
subl %r14d, %edx
jmp 0x18e0
leaq -0x38(%rbp), %rdi
callq 0x12f2
testl %eax, %eax
jne 0x18eb
addl 0x4(%r13,%r14,8), %ebx
jmp 0x18eb
movl 0x4(%r13,%r14,8), %eax
addl %eax, %ebx
decl %ebx
jmp 0x18eb
leaq -0x38(%rbp), %rdi
callq 0x12f2
movl %eax, %r14d
leaq -0x38(%rbp), %rdi
callq 0x12f2
xorl %edx, %edx
cmpl %r14d, %eax
setle %dl
leaq -0x38(%rbp), %rdi
movl %ebx, %esi
callq 0x12e0
incl %ebx
jmp 0x149c
leaq -0x38(%rbp), %rdi
callq 0x1302
leaq -0xd0(%rbp), %rdi
callq 0x1090
leaq -0x98(%rbp), %rdi
callq 0x1090
leaq -0x60(%rbp), %rdi
callq 0x1a4a
movq -0xa0(%rbp), %rsp
xorl %eax, %eax
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x26a5(%rip), %rax      # 0x3fe0
movq (%rax), %rcx
leaq 0x72f(%rip), %rdi       # 0x2074
pushq $0x1d
jmp 0x1971
movq 0x2690(%rip), %rax      # 0x3fe0
movq (%rax), %rcx
leaq 0x73a(%rip), %rdi       # 0x2094
pushq $0x13
jmp 0x1971
movq 0x267b(%rip), %rax      # 0x3fe0
movq (%rax), %rcx
leaq 0x749(%rip), %rdi       # 0x20b8
pushq $0x16
popq %rsi
pushq $0x1
popq %rdx
callq 0x11a0
jmp 0x1923
movq %rax, %rbx
jmp 0x1991
jmp 0x1985
jmp 0x19ab
movq %rax, %rbx
leaq -0x38(%rbp), %rdi
callq 0x1302
leaq -0xd0(%rbp), %rdi
callq 0x1090
leaq -0x98(%rbp), %rdi
callq 0x1090
jmp 0x19ae
movq %rax, %rbx
leaq -0x60(%rbp), %rdi
callq 0x1a4a
movq %rbx, %rdi
callq 0x1170
nop