#!/usr/bin/awk -f


# Stack: DROP, DUP, OVER, PUSH, POP
# Math:	+ AND XOR NOT 2* 2/ multiply-step
# Call:	JUMP CALL RETURN IF -IF
# Loop:	NEXT UNEXT
# Register:	A A! B!
# Memory: @ ! @+ !+ @B !B @P !P
# NO-OP: .


BEGIN {
    # Initialize VM state
    stack_pointer = 0    # Points to next free position
    pc = 0               # Program counter
    MAX_STACK = 100      # Maximum stack size
    MAX_MEM = 1000       # Memory size
    
    # Initialize registers
    A = 0              # A register
    B = 0              # B register
    P = 0              # P register (auxiliary pointer)
    
    # Stack operations
    split("", stack)   # Initialize stack array
    split("", memory)  # Initialize memory array
}

# Stack operations
function push(value) {
    if (stack_pointer >= MAX_STACK) {
        print "Stack overflow!" > "/dev/stderr"
        exit 1
    }
    printf "# DEBUG: Pushing %d onto stack\n", value > "/dev/stderr"
    stack[stack_pointer++] = value
}

function pop() {
    if (stack_pointer <= 0) {
        print "Stack underflow!" > "/dev/stderr"
        exit 1
    }
    value = stack[--stack_pointer]
    printf "# DEBUG: Popping %d from stack\n", value > "/dev/stderr"
    return value
}

# Basic stack manipulation
function op_drop() {
    pop()
}

function op_dup() {
    if (stack_pointer <= 0) {
        print "Stack underflow on DUP!" > "/dev/stderr"
        exit 1
    }
    push(stack[stack_pointer - 1])
}

function op_over() {
    if (stack_pointer <= 1) {
        print "Stack underflow on OVER!" > "/dev/stderr"
        exit 1
    }
    push(stack[stack_pointer - 2])
}

# Basic arithmetic operations
function op_add() {
    b = pop()
    a = pop()
    result = a + b
    printf "# DEBUG: Adding %d + %d = %d\n", a, b, result > "/dev/stderr"
    push(result)
}

function op_and() {
    b = pop()
    a = pop()
    # For now, we'll just multiply as a placeholder
    # In a real implementation, we'd need to implement proper bitwise AND
    push(a * b)
}

function op_xor() {
    b = pop()
    a = pop()
    # For now, we'll just add as a placeholder
    # In a real implementation, we'd need to implement proper bitwise XOR
    push(a + b)
}

function op_not() {
    a = pop()
    # For now, we'll just negate as a placeholder
    # In a real implementation, we'd need to implement proper bitwise NOT
    push(-a - 1)
}

function op_2times() {
    a = pop()
    push(a * 2)
}

function op_2div() {
    a = pop()
    push(int(a / 2))
}

function op_multiply() {
    b = pop()
    a = pop()
    result = a * b
    printf "# DEBUG: Multiplying %d * %d = %d\n", a, b, result > "/dev/stderr"
    push(result)
}

# Register operations
function op_a() {
    push(A)
}

function op_astore() {
    A = pop()
}

function op_bstore() {
    B = pop()
}

# Memory operations
function op_fetch() {
    addr = pop()
    value = memory[addr]
    printf "# DEBUG: Fetching %d from memory[%d]\n", value, addr > "/dev/stderr"
    push(value)
}

function op_store() {
    addr = pop()  # First pop the address
    value = pop() # Then pop the value
    printf "# DEBUG: Storing %d at memory[%d]\n", value, addr > "/dev/stderr"
    memory[addr] = value
}

function op_fetchplus() {
    push(memory[P++])
}

function op_storeplus() {
    memory[P++] = pop()
}

function op_fetchb() {
    push(memory[B])
}

function op_storeb() {
    memory[B] = pop()
}

function op_fetchp() {
    push(memory[P])
}

function op_storep() {
    memory[P] = pop()
}

function print_stack() {
    printf "Stack: "
    for (i = 0; i < stack_pointer; i++) {
        printf "%d ", stack[i]
    }
    printf "\n"
}

function print_state() {
    print_stack()
    printf "Registers: A=%d B=%d P=%d\n", A, B, P
    printf "Memory[P]=%d Memory[B]=%d\n", memory[P], memory[B]
    printf "Memory: "
    for (i = 0; i < 4; i++) {  # Show first 4 memory locations
        printf "[%d]=%d ", i, memory[i]
    }
    printf "\n-------------------\n"
}

function op_swap() {
    if (stack_pointer < 2) {
        print "Stack underflow on SWAP!" > "/dev/stderr"
        exit 1
    }
    # Swap the top two elements on the stack
    temp = stack[stack_pointer - 1]
    stack[stack_pointer - 1] = stack[stack_pointer - 2]
    stack[stack_pointer - 2] = temp
    printf "# DEBUG: Swapping top two stack elements\n" > "/dev/stderr"
}

function execute_instruction(inst) {
    if (inst ~ /^[0-9]+$/) {
        # Numbers are pushed onto the stack
        push(int(inst))
        return
    }
    
    if (inst == "BYE")      { exit 0 } # not really in the minimal spec as set out by Chuck Moore, but useful for a graceful exit.
    if (inst == "DROP")     { op_drop(); return }
    if (inst == "DUP")      { op_dup(); return }
    if (inst == "OVER")     { op_over(); return } # copy second item to top of stack
    if (inst == "SWAP")     { op_swap(); return } # swap top two stack items
    if (inst == "+")        { op_add(); return }
    if (inst == "AND")      { op_and(); return }
    if (inst == "XOR")      { op_xor(); return }
    if (inst == "NOT")      { op_not(); return }
    if (inst == "2*")       { op_2times(); return } # multiply-step
    if (inst == "2/")       { op_2div(); return } # divide-step
    if (inst == "*")        { op_multiply(); return }
    if (inst == "A")        { op_a(); return } # push A register
    if (inst == "A!")       { op_astore(); return } # store A register
    if (inst == "B!")       { op_bstore(); return } # store B register
    if (inst == "@")        { op_fetch(); return } # fetch from memory
    if (inst == "!")        { op_store(); return } # store to memory
    if (inst == "@+")       { op_fetchplus(); return } # fetch from memory at P+
    if (inst == "!+")       { op_storeplus(); return } # store to memory at P+
    if (inst == "@B")       { op_fetchb(); return } # fetch from memory at B
    if (inst == "!B")       { op_storeb(); return } # store to memory at B
    if (inst == "@P")       { op_fetchp(); return } # fetch from memory at P
    if (inst == "!P")       { op_storep(); return } # store to memory at P
    if (inst == ".")        { return }  # NO-OP
    if (inst == "SHOW")     { print_state(); return } # show state info
    
    print "Unknown instruction: " inst > "/dev/stderr"
    exit 1
}

# Main execution loop
{
    # Remove comments (everything after #)
    sub(/#.*$/, "")
    
    # Skip empty lines after comment removal
    if (NF == 0) next
    
    # Split the input line into words
    n = split($0, words)
    for (i = 1; i <= n; i++) {
        execute_instruction(words[i])
    }
}