Files

161 lines
3.5 KiB
Bash

#!/usr/bin/env bash
adv () { # OPCODE 0
$DEBUG && printf "OPCODE : 0 OP: adv OPERAND: %s\n" "$1" >&2
OPERAND=$( get_combo "$1" )
#REG_A=$(( REG_A / (2 ** OPERAND) ))
REG_A=$( printf "%s / (2 ^ %s) \n" "$REG_A" "$OPERAND" | bc )
}
bxl () { # OPCODE 1
$DEBUG && printf "OPCODE : 1 OP: bxl OPERAND: %s\n" "$1" >&2
OPERAND=$1
REG_B=$(( REG_B ^ OPERAND ))
}
bst () { # OPCODE 2
$DEBUG && printf "OPCODE : 2 OP: bst OPERAND: %s\n" "$1" >&2
OPERAND=$( get_combo "$1" )
REG_B=$(( OPERAND % 8 ))
}
jnz () { # OPCODE 3
$DEBUG && printf "OPCODE : 3 OP: jnz OPERAND: %s\n" "$1" >&2
OPERAND=$1
if [[ $REG_A -eq 0 ]]
then
return 0
else
POINTER=$OPERAND
fi
}
bxc () { # OPCODE 4
$DEBUG && printf "OPCODE : 4 OP: bxc OPERAND: %s\n" "$1" >&2
# OPERAND=$1 # Deprecated for legacy reasons
REG_B=$(( REG_B ^ REG_C ))
}
out () { # OPCODE 5
$DEBUG && printf "OPCODE : 5 OP: out OPERAND: %s\n" "$1" >&2
OPERAND=$( get_combo "$1" )
printf "%s\n" "$(( OPERAND % 8 ))"
}
bdv () { # OPCODE 6
$DEBUG && printf "OPCODE : 6 OP: bdv OPERAND: %s\n" "$1" >&2
OPERAND=$( get_combo "$1" )
#REG_B=$(( REG_A / (2 ** OPERAND) ))
REG_B=$( printf "%s / (2 ^ %s) \n" "$REG_A" "$OPERAND" | bc )
}
cdv () { # OPCODE 7
$DEBUG && printf "OPCODE : 7 OP: cdv OPERAND: %s\n" "$1" >&2
OPERAND=$( get_combo "$1" )
#REG_C=$(( REG_A / (2 ** OPERAND) ))
REG_C=$( printf "%s / (2 ^ %s) \n" "$REG_A" "$OPERAND" | bc )
}
# Get combo operand
get_combo () {
if [[ $OPERAND -eq 0 ]]
then
printf "$1"
elif [[ $OPERAND -eq 1 ]]
then
printf "$1"
elif [[ $OPERAND -eq 2 ]]
then
printf "$1"
elif [[ $OPERAND -eq 3 ]]
then
printf "$1"
elif [[ $OPERAND -eq 4 ]]
then
printf "%s" "$REG_A"
elif [[ $OPERAND -eq 5 ]]
then
printf "%s" "$REG_B"
elif [[ $OPERAND -eq 6 ]]
then
printf "%s" "$REG_C"
elif [[ $OPERAND -eq 7 ]]
then
printf "Reserved operand. Exiting.\n" >&2
exit 1
fi
}
# Check if the program is a quine
check_quine () {
$DEBUG && printf "%s," "${INPUT[@]}"
$DEBUG && printf "%s," "${OUTPUT[@]}"
for (( i=0; i<${#INPUT[@]}; i++ ))
do
if [[ ${INPUT[$i]} != "${OUTPUT[$i]}" ]]
then
return 1
fi
done
}
# Check digits
check_tail () {
$DEBUG && printf "%s," "${INPUT[@]}"
$DEBUG && printf "%s," "${OUTPUT[@]}"
for (( i=${#INPUT[@]}; i>=${#INPUT[@]}-$1; i-- ))
do
if [[ ${INPUT[$i]} != "${OUTPUT[$i]}" ]]
then
return 1
fi
done
}
# Interpret opcode
read_opcode () {
local OPCODE=$1
local OPERAND=$2
if [[ $OPCODE -eq 0 ]]
then
adv "$OPERAND"
(( POINTER+=2 ))
elif [[ $OPCODE -eq 1 ]]
then
bxl "$OPERAND"
(( POINTER+=2 ))
elif [[ $OPCODE -eq 2 ]]
then
bst "$OPERAND"
(( POINTER+=2 ))
elif [[ $OPCODE -eq 3 ]]
then
# jnz itself moves the pointer
jnz "$OPERAND"
elif [[ $OPCODE -eq 4 ]]
then
bxc "$OPERAND"
(( POINTER+=2 ))
elif [[ $OPCODE -eq 5 ]]
then
out "$OPERAND"
(( POINTER+=2 ))
elif [[ $OPCODE -eq 6 ]]
then
bdv "$OPERAND"
(( POINTER+=2 ))
elif [[ $OPCODE -eq 7 ]]
then
cdv "$OPERAND"
(( POINTER+=2 ))
fi
}
execute_machine () {
POINTER=0
while [[ $POINTER -lt $INPUT_LEN ]]
do
OPCODE=${INPUT[$POINTER]}
OPERAND=${INPUT[$POINTER+1]}
read_opcode "$OPCODE" "$OPERAND"
$DEBUG && printf "Registers A : %s B: %s C : %s\n" "$REG_A" "$REG_B" "$REG_C" >&2
$DEBUG && printf "Pointer : %s\n" "$POINTER" >&2
(( MACHINE_ITER-- ))
if [[ $MACHINE_ITER -eq 0 ]] ; then break ; fi
done | paste -s -d " "
}