Skip to content

Instantly share code, notes, and snippets.

@userlandkernel
Last active March 14, 2022 03:27
Show Gist options
  • Save userlandkernel/68670a2eb7504ed2aa1bf2b1b8f529ce to your computer and use it in GitHub Desktop.
Save userlandkernel/68670a2eb7504ed2aa1bf2b1b8f529ce to your computer and use it in GitHub Desktop.
Bash assembly emulator
function MMU_ERROR(){
echo -e "MMU_PANIC: $1" >&2;
while true;do
SPIN_FOREVER=1;
done
exit 1;
}
function JIT_ERROR(){
echo -e "JIT Error: $1" >&2;
exit 1;
}
function EMULATOR() {
MEMORY_BASE=0x1000000
MEMORY_END=0
declare -a ARM64_GPREGS
function ARM64_REG() {
for REG in ${ARM64_GPREGS[@]};do
if [[ "REG_$1" == "$REG" ]]; then
return 0;
fi
done
return 1;
}
function MEMORY_INIT() {
echo "Initializing memory..." >&2;
declare -a PAGEMAP
function _ml_io_map() {
PAGEMAP[$1]=0
}
echo "Setting up pages..." >&2;
for addr in $(seq 0 1 $((0x4000))); do
_ml_io_map $(( MEMORY_BASE + $(($addr)) ));
MEMORY_END=$(( MEMORY_BASE + $(($addr)) ));
done
unset -f _ml_io_map
echo "Memory initialized." >&2;
}
function PAGEFAULT(){
printf "SEGMENTATION_FAULT: not paged %#llx\n" "$1" >&2;
}
function PAGECHECK() {
MEMORY_BASE=0x1000000 # You cant fool me!
# Do not modify the memory to be below the memory base you cant fool me
if [ $(($MEMORY_END)) -lt $(($MEMORY_BASE)) ]; then
MMU_ERROR "Don't hack the memory!"
fi
# Check if outside memory range
if [ $(($1)) -lt $(($MEMORY_BASE)) ]; then
PAGEFAULT $(($1));
exit 1
elif [ $(($1)) -gt $(($MEMORY_END)) ]; then
PAGEFAULT $(($1));
exit 1
fi
}
# read at given address
function MACHINE_READ(){
PAGECHECK $(($1)) # Check if in pagemap
echo ${PAGEMAP[$(($1))]}; # Read
}
function MACHINE_WRITE(){
PAGECHECK $(($1)) # Check if in pagemap
PAGEMAP[$(($1))]=$(($2)); # Write
}
# Get register value
function CPU_GET_REG(){
REG="REG_$1"
VALUE="$(eval "echo \$$REG")"
echo $(($VALUE))
}
# Set register value
function CPU_SET_REG(){
REG="REG_$1"
VALUE="$2"
eval "$REG=$(($VALUE))"
}
# MOV instruction
function MOV() {
DSTREG="$1"
IMMEDIATE="$2"
if ! ARM64_REG $DSTREG; then
JIT_ERROR "Invalid destination register '$DSTREG'";
fi
if ARM64_REG $IMMEDIATE; then
CPU_SET_REG $DSTREG $(CPU_GET_REG $IMMEDIATE)
else
CPU_SET_REG $DSTREG $(($IMMEDIATE))
fi
}
# ADD instruction
function ADD() {
DSTREG="$1"
IMMEDIATE_A="$2"
IMMEDIATE_B="$3"
if ! ARM64_REG $DSTREG; then
JIT_ERROR "Invalid destination register '$DSTREG'";
fi
if ARM64_REG $IMMEDIATE_A; then
CPU_SET_REG $DSTREG $(( $(CPU_GET_REG $IMMEDIATE_A) ))
else
CPU_SET_REG $DSTREG $(( $IMMEDIATE_A ))
fi
if ARM64_REG $IMMEDIATE_B; then
CPU_SET_REG $DSTREG $(( $(( $(CPU_GET_REG $DSTREG) )) + $(($(CPU_GET_REG $IMMEDIATE_B))) ))
else
CPU_SET_REG $DSTREG $(( $(( $(CPU_GET_REG $DSTREG) )) + $(( $IMMEDIATE_B )) ))
fi
}
# SUB instruction
function SUB() {
DSTREG="$1"
IMMEDIATE_A="$2"
IMMEDIATE_B="$3"
if ! ARM64_REG $DSTREG; then
JIT_ERROR "Invalid destination register '$DSTREG'";
fi
if ARM64_REG $IMMEDIATE_A; then
CPU_SET_REG $DSTREG $(( $(CPU_GET_REG $IMMEDIATE_A) ))
else
CPU_SET_REG $DSTREG $(( $IMMEDIATE_A ))
fi
if ARM64_REG $IMMEDIATE_B; then
CPU_SET_REG $DSTREG $(( $(( $(CPU_GET_REG $DSTREG) )) - $(($(CPU_GET_REG $IMMEDIATE_B))) ))
else
CPU_SET_REG $DSTREG $(( $(( $(CPU_GET_REG $DSTREG) )) - $(( $IMMEDIATE_B )) ))
fi
}
# LDR instruction
function LDR() {
DSTREG="$1"
SRCREG="$2"
if ! ARM64_REG $DSTREG; then
JIT_ERROR "Invalid destination register '$DSTREG'";
fi
if ! ARM64_REG $SRCREG; then
JIT_ERROR "Invalid destination register '$SRCREG'";
fi
VALUE=$(($(MACHINE_READ $(CPU_GET_REG $SRCREG))))
CPU_SET_REG $DSTREG $VALUE
}
function STR() {
DSTREG="$1"
SRCREG="$2"
if ! ARM64_REG $DSTREG; then
JIT_ERROR "Invalid destination register '$DSTREG'";
fi
if ! ARM64_REG $SRCREG; then
JIT_ERROR "Invalid destination register '$SRCREG'";
fi
ADDR=$(($(CPU_GET_REG $DSTREG)))
VALUE=$(($(CPU_GET_REG $SRCREG)))
MACHINE_WRITE $ADDR $VALUE
}
function BL() {
return 0;
}
# Branch instruction
function B() {
return 0;
}
# Debug instruction for printing regs
function PRINT_REG() {
printf "%d / %#llx\n" "$(CPU_GET_REG $1)" "$(CPU_GET_REG $1)"
}
# CPU Initialization
function CPU_INIT() {
echo "Initializing CPU..."
declare -i ARM64_FLAGREG # Condition flag register
# Create and intialize general purpose registers
for i in $(seq 0 1 31);do
ARM64_GPREGS+="REG_X$i "
declare -i "REG_X$i";
eval REG_X$i[$i]=0;
done
echo "CPU initialized."
return 0;
}
# Initialize CPU and memory
CPU_INIT
MEMORY_INIT
# Undefine the init functions again
unset -f CPU_INIT
unset -f MEMORY_INIT
# Start the interpreter
while true;do
echo -e ">>> " | tr -d '\n'
read ASM
eval "$ASM"
done
return 0;
}
EMULATOR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment