Last active
March 14, 2022 03:27
-
-
Save userlandkernel/68670a2eb7504ed2aa1bf2b1b8f529ce to your computer and use it in GitHub Desktop.
Bash assembly emulator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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