summaryrefslogtreecommitdiff
path: root/asm.sh
diff options
context:
space:
mode:
authorSebastian G. Kirmayer <gloria@gloria-mundi.eu>2026-07-07 23:54:15 +0200
committerSebastian G. Kirmayer <gloria@gloria-mundi.eu>2026-07-07 23:54:15 +0200
commit9a6c7d85eb1be819b94d0feb0cb84de8546e36fd (patch)
tree73bab5bccdeffed245bd596cdeadde5cfddeb42f /asm.sh
Diffstat (limited to 'asm.sh')
-rw-r--r--asm.sh1133
1 files changed, 1133 insertions, 0 deletions
diff --git a/asm.sh b/asm.sh
new file mode 100644
index 0000000..1ee8ecb
--- /dev/null
+++ b/asm.sh
@@ -0,0 +1,1133 @@
+# Copyright (C) 2026 Sebastian G. Kirmayer <gloria@gloria-mundi.eu>
+#
+# This file is part of sysf-i386.
+#
+# sysf-i386 is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# sysf-i386 is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along
+# with sysf-i386. If not, see <https://www.gnu.org/licenses/>.
+#
+
+LC_ALL=POSIX
+
+_asm_cc() {
+ _asm_ccval=0
+ for _asm_ccs in o no "b c nae" "nb nc ae" "e z" "ne nz" "be na" "nbe a" \
+ s ns "p pe" "np po" "l nge" "nl ge" "le ng" "nle g"
+ do
+ for _asm_cc in $_asm_ccs
+ do
+ eval "$1$_asm_cc() { _asm_$1 $_asm_ccval \"\$@\" ; }"
+ done
+ : $(( _asm_ccval += 1 ))
+ done
+}
+
+addl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst in
+ imm/reg|imm/mem)
+ if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ]
+ then
+ byte 0x83
+ _asm_modrm 0 _asm_dst
+ byte $_asm_src_imm
+ elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ]
+ then
+ byte 0x05
+ long $_asm_src_imm
+ else
+ byte 0x81
+ _asm_modrm 0 _asm_dst
+ long $_asm_src_imm
+ fi
+ ;;
+ reg/reg|reg/mem)
+ byte 0x01
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x03
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "addl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+andl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst/$_asm_dst_reg in
+ imm/reg/0)
+ byte 0x25
+ long $_asm_src_imm
+ ;;
+ imm/reg/*|imm/mem/*)
+ byte 0x81
+ _asm_modrm 4 _asm_dst
+ long $_asm_src_imm
+ ;;
+ reg/reg/*|reg/mem/*)
+ byte 0x21
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg/*|mem/reg/*)
+ byte 0x23
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "andl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+cmpb() {
+ _asm_src8 "$1"
+ _asm_dst8 "$2"
+ case $_asm_src/$_asm_dst/$_asm_dst_reg in
+ imm/reg/0)
+ byte 0x3C
+ byte $_asm_src_imm
+ ;;
+ imm/reg/*|imm/mem/*)
+ byte 0x80
+ _asm_modrm 7 _asm_dst
+ byte $_asm_src_imm
+ ;;
+ reg/reg|reg/mem)
+ byte 0x38
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x3A
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "cmpb $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+cmpl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst in
+ imm/reg|imm/mem)
+ if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ]
+ then
+ byte 0x83
+ _asm_modrm 7 _asm_dst
+ byte $_asm_src_imm
+ elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ]
+ then
+ byte 0x3D
+ long $_asm_src_imm
+ else
+ byte 0x81
+ _asm_modrm 7 _asm_dst
+ long $_asm_src_imm
+ fi
+ ;;
+ reg/reg|reg/mem)
+ byte 0x39
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x3B
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "cmpl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+cmpsb() {
+ byte 0xA6
+}
+
+call() {
+ _asm_dst "$1"
+ case $_asm_dst in
+ reg|mem)
+ byte 0xFF
+ _asm_modrm 2 _asm_dst
+ return
+ ;;
+ imm)
+ ;;
+ *)
+ _asm_panic "call $_asm_dst: unsupported"
+ esac
+
+ _asm_jmp_dist=$(( _asm_dst_imm - _asm_offset ))
+ byte 0xE8
+ long $((_asm_jmp_dist-5))
+}
+
+_asm_cc cmov
+_asm_cmov() {
+ _asm_src "$2"
+ _asm_dst "$3"
+ case $_asm_src/$_asm_dst in
+ reg/reg|mem/reg)
+ byte 0x0F
+ byte $(( 0x40 | $1 ))
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "cmov $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+decl() {
+ _asm_dst "$1"
+ case $_asm_dst in
+ reg)
+ byte $(( 0x48 | _asm_dst_reg ))
+ ;;
+ reg|mem)
+ byte 0xFF
+ _asm_modrm 1 _asm_dst
+ ;;
+ *)
+ _asm_panic "decl $_asm_dst: unsupported"
+ esac
+}
+
+divl() {
+ _asm_src "$1"
+ case $_asm_src in
+ reg|mem)
+ byte 0xF7
+ _asm_modrm 6 _asm_src
+ ;;
+ *)
+ _asm_panic "divl $_asm_src: unsupported"
+ esac
+}
+
+incl() {
+ _asm_dst "$1"
+ case $_asm_dst in
+ reg)
+ byte $(( 0x40 | _asm_dst_reg ))
+ ;;
+ reg|mem)
+ byte 0xFF
+ _asm_modrm 0 _asm_dst
+ ;;
+ *)
+ _asm_panic "incl $_asm_dst: unsupported"
+ esac
+}
+
+incb() {
+ _asm_dst8 "$1"
+ case $_asm_dst in
+ reg|mem)
+ byte 0xFE
+ _asm_modrm 0 _asm_dst
+ ;;
+ *)
+ _asm_panic "incb $_asm_dst: unsupported"
+ esac
+}
+
+int() {
+ byte 0xCD
+ byte "$1"
+}
+
+int3() {
+ byte 0xCC
+}
+
+_asm_cc j
+_asm_j() {
+ _asm_eval "$2"
+ _asm_jmp_dist=$(( _asm_value - _asm_offset ))
+ if [ $(( -0x80 <= _asm_jmp_dist-2 && _asm_jmp_dist-2 < 0x80 )) != 0 ]
+ then
+ byte $(( 0x70 | $1 ))
+ byte $((_asm_jmp_dist-2))
+ else
+ byte 0x0F
+ byte $(( 0x80 | $1 ))
+ long $((_asm_jmp_dist-6))
+ fi
+}
+
+jmp() {
+ _asm_dst "$1"
+ case $_asm_dst in
+ reg|mem)
+ byte 0xFF
+ _asm_modrm 4 _asm_dst
+ return
+ ;;
+ imm)
+ ;;
+ *)
+ _asm_panic "jmp $_asm_dst: unsupported"
+ esac
+
+ _asm_jmp_dist=$(( _asm_dst_imm - _asm_offset ))
+ if [ $(( -0x80 <= _asm_jmp_dist-2 && _asm_jmp_dist-2 < 0x80 )) != 0 ]
+ then
+ byte 0xEB
+ byte $((_asm_jmp_dist-2))
+ else
+ byte 0xE9
+ long $((_asm_jmp_dist-5))
+ fi
+}
+
+leal() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst in
+ mem/reg)
+ byte 0x8D
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "leal $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+movb() {
+ _asm_src8 "$1"
+ _asm_dst8 "$2"
+ case $_asm_src/$_asm_dst in
+ reg/reg|reg/mem)
+ byte 0x88
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x8A
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ imm/reg)
+ byte $(( 0xB0 | _asm_dst_reg ))
+ byte $_asm_src_imm
+ ;;
+ imm/reg|imm/mem)
+ byte 0xC6
+ _asm_modrm 0 _asm_dst
+ byte $_asm_src_imm
+ ;;
+ *)
+ _asm_panic "movb $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+movl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst in
+ reg/reg|reg/mem)
+ byte 0x89
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x8B
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ imm/reg)
+ byte $(( 0xB8 | _asm_dst_reg ))
+ long $_asm_src_imm
+ ;;
+ imm/reg|imm/mem)
+ byte 0xC7
+ _asm_modrm 0 _asm_dst
+ long $_asm_src_imm
+ ;;
+ *)
+ _asm_panic "movl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+mull() {
+ _asm_src "$1"
+ case $_asm_src in
+ reg|mem)
+ byte 0xF7
+ _asm_modrm 4 _asm_src
+ ;;
+ *)
+ _asm_panic "mull $_asm_src: unsupported"
+ esac
+}
+
+negl() {
+ _asm_dst "$1"
+ case $_asm_dst in
+ reg|mem)
+ byte 0xF7
+ _asm_modrm 3 _asm_dst
+ ;;
+ *)
+ _asm_panic "negl $_asm_dst: unsupported"
+ esac
+}
+
+nop() {
+ byte 0x90
+}
+
+notl() {
+ _asm_dst "$1"
+ case $_asm_dst in
+ reg|mem)
+ byte 0xF7
+ _asm_modrm 2 _asm_dst
+ ;;
+ *)
+ _asm_panic "notl $_asm_dst: unsupported"
+ esac
+}
+
+orl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst in
+ imm/reg|imm/mem)
+ if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ]
+ then
+ byte 0x83
+ _asm_modrm 1 _asm_dst
+ byte $_asm_src_imm
+ elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ]
+ then
+ byte 0x0D
+ long $_asm_src_imm
+ else
+ byte 0x81
+ _asm_modrm 1 _asm_dst
+ long $_asm_src_imm
+ fi
+ ;;
+ reg/reg|reg/mem)
+ byte 0x09
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x0B
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "orl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+orb() {
+ _asm_src8 "$1"
+ _asm_dst8 "$2"
+ case $_asm_src/$_asm_dst in
+ imm/reg|imm/mem)
+ if [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ]
+ then
+ byte 0x0C
+ long $_asm_src_imm
+ else
+ byte 0x80
+ _asm_modrm 1 _asm_dst
+ long $_asm_src_imm
+ fi
+ ;;
+ reg/reg|reg/mem)
+ byte 0x08
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x0A
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "orb $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+popl() {
+ _asm_dst "$1"
+ case $_asm_dst in
+ reg)
+ byte $((0x58 | _asm_dst_reg))
+ ;;
+ reg|mem)
+ byte 0x8F
+ _asm_modrm 0 _asm_dst
+ ;;
+ *)
+ _asm_panic "popl $_asm_src: unsupported"
+ esac
+}
+
+pushl() {
+ _asm_src "$1"
+ case $_asm_src in
+ reg)
+ byte $((0x50 | _asm_src_reg))
+ ;;
+ reg|mem)
+ byte 0xFF
+ _asm_modrm 6 _asm_src
+ ;;
+ imm)
+ byte 0x68
+ long $_asm_src_imm
+ ;;
+ *)
+ _asm_panic "pushl $_asm_src: unsupported"
+ esac
+}
+
+repnz() {
+ byte 0xF2
+ "$@"
+}
+
+repz() {
+ byte 0xF3
+ "$@"
+}
+
+ret() {
+ if [ $# -eq 0 ]
+ then
+ byte 0xC3
+ else
+ byte 0xC2
+ word "$1"
+ fi
+}
+
+scasb() {
+ byte 0xAE
+}
+
+shll() {
+ _asm_src8 "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_src_reg/$_asm_dst in
+ imm/*/reg|imm/*/mem)
+ if [ $_asm_src_imm = 1 ] ; then
+ byte 0xD1
+ _asm_modrm 4 _asm_dst
+ else
+ byte 0xC1
+ _asm_modrm 4 _asm_dst
+ byte $_asm_src_imm
+ fi
+ ;;
+ reg/1/reg|reg/1/mem)
+ byte 0xD3
+ _asm_modrm 4 _asm_dst
+ ;;
+ *)
+ _asm_panic "shll $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+shrl() {
+ _asm_src8 "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_src_reg/$_asm_dst in
+ imm/*/reg|imm/*/mem)
+ if [ $_asm_src_imm = 1 ] ; then
+ byte 0xD1
+ _asm_modrm 5 _asm_dst
+ else
+ byte 0xC1
+ _asm_modrm 5 _asm_dst
+ byte $_asm_src_imm
+ fi
+ ;;
+ reg/1/reg|reg/1/mem)
+ byte 0xD3
+ _asm_modrm 5 _asm_dst
+ ;;
+ *)
+ _asm_panic "shrl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+subl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst in
+ imm/reg|imm/mem)
+ if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ]
+ then
+ byte 0x83
+ _asm_modrm 5 _asm_dst
+ byte $_asm_src_imm
+ elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ]
+ then
+ byte 0x2D
+ long $_asm_src_imm
+ else
+ byte 0x81
+ _asm_modrm 5 _asm_dst
+ long $_asm_src_imm
+ fi
+ ;;
+ reg/reg|reg/mem)
+ byte 0x29
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x2B
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "subl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+test() {
+ _asm_panic "test requires width suffix"
+}
+
+testl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst/$_asm_dst_reg in
+ imm/reg/0)
+ byte 0xA9
+ long $_asm_src_imm
+ ;;
+ imm/reg/*|imm/mem/*)
+ byte 0xF7
+ _asm_modrm 0 _asm_dst
+ long $_asm_src_imm
+ ;;
+ reg/reg/*|reg/mem/*)
+ byte 0x85
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ *)
+ _asm_panic "testl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+ud2() {
+ byte 0x0F
+ byte 0x0B
+}
+
+xchgl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst/$_asm_src_reg/$_asm_dst_reg in
+ reg/reg/0/*)
+ byte $((0x90 + _asm_dst_reg))
+ ;;
+ reg/reg/*/0)
+ byte $((0x90 + _asm_src_reg))
+ ;;
+ reg/reg/*|mem/reg/*)
+ byte 0x87
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ reg/reg/*|reg/mem/*)
+ byte 0x87
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ *)
+ _asm_panic "xchgl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+xorl() {
+ _asm_src "$1"
+ _asm_dst "$2"
+ case $_asm_src/$_asm_dst in
+ imm/reg|imm/mem)
+ if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ]
+ then
+ byte 0x83
+ _asm_modrm 6 _asm_dst
+ byte $_asm_src_imm
+ elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ]
+ then
+ byte 0x35
+ long $_asm_src_imm
+ else
+ byte 0x81
+ _asm_modrm 6 _asm_dst
+ long $_asm_src_imm
+ fi
+ ;;
+ reg/reg|reg/mem)
+ byte 0x31
+ _asm_modrm $_asm_src_reg _asm_dst
+ ;;
+ reg/reg|mem/reg)
+ byte 0x33
+ _asm_modrm $_asm_dst_reg _asm_src
+ ;;
+ *)
+ _asm_panic "xorl $_asm_src $_asm_dst: unsupported"
+ esac
+}
+
+byte() {
+ : $(( _asm_offset += 1 ))
+ _asm_eval "$1"
+ _asm_printf "\\$(printf %03o $(( _asm_value & 0xFF )))"
+}
+
+word() {
+ _asm_eval "$1"
+ _asm_word=$_asm_value
+ byte $(( _asm_word ))
+ byte $(( _asm_word >> 8 ))
+}
+
+long() {
+ _asm_eval "$1"
+ _asm_long=$_asm_value
+ word $(( _asm_long ))
+ word $(( _asm_long >> 16 ))
+}
+
+align() {
+ _asm_align=${1-4}
+ skip $(( (_asm_align - (_asm_offset % _asm_align)) % _asm_align ))
+}
+
+skip() {
+ _asm_eval "$1"
+ : $(( _asm_offset += _asm_value ))
+ if [ $_asm_active != 0 ] ; then
+ while [ $_asm_value -gt 0 ] ; do
+ printf '\0'
+ : $(( _asm_value -= 1 ))
+ done
+ fi
+}
+
+ascii() {
+ : $(( _asm_offset += ${#1} ))
+ _asm_printf '%s' "$1"
+}
+
+_asm_modrm() {
+ eval "_asm_modrm_rm=\$$2"
+ case $_asm_modrm_rm in
+ reg)
+ byte $(( 0xC0 | $1 << 3 | $2_reg ))
+ return
+ ;;
+ mem)
+ ;;
+ *)
+ _asm_panic "modrm $_asm_modrm_rm: unsupported"
+ esac
+
+ for part in base index scale displ ; do
+ eval "_asm_modrm_$part=\$$2_mem_$part"
+ done
+
+ if [ -z "$_asm_modrm_displ" ] && [ "$_asm_modrm_base" != 5 ] ; then
+ _asm_modrm_displsize=0
+ elif [ $(( -128 <= _asm_modrm_displ && _asm_modrm_displ < 128 )) != 0 ]
+ then
+ _asm_modrm_displsize=1
+ else
+ _asm_modrm_displsize=2
+ fi
+
+ if [ -z "$_asm_modrm_index" ] && [ "$_asm_modrm_base" != 4 ] ; then
+ if [ -z "$_asm_modrm_base" ] ; then
+ byte $(( $1 << 3 | 5 ))
+ _asm_modrm_displsize=2
+ else
+ byte $(( _asm_modrm_displsize << 6 | $1 << 3 | _asm_modrm_base ))
+ fi
+ else
+ if [ -z "$_asm_modrm_base" ] ; then
+ byte $(( $1 << 3 | 4 ))
+ _asm_modrm_displsize=2
+ _asm_modrm_base=5
+ else
+ byte $(( _asm_modrm_displsize << 6 | $1 << 3 | 4 ))
+ fi
+ [ "$_asm_modrm_index" ] || _asm_modrm_index=4
+ byte $(( _asm_modrm_scale << 6 | _asm_modrm_index << 3 | _asm_modrm_base ))
+ fi
+ case $_asm_modrm_displsize in
+ 0)
+ ;;
+ 1)
+ byte $((_asm_modrm_displ))
+ ;;
+ 2)
+ long $((_asm_modrm_displ))
+ ;;
+ esac
+}
+
+_asm_src8() {
+ _asm_op8 "$1" "_asm_src$2"
+}
+
+_asm_src() {
+ _asm_op "$1" "_asm_src$2"
+}
+
+_asm_dst8() {
+ _asm_op8 "$1" _asm_dst
+}
+
+_asm_dst() {
+ _asm_op "$1" _asm_dst
+}
+
+_asm_op8() {
+ case "$1" in
+ %*)
+ eval "$2=reg"
+ _asm_register8 "${1#%}" "$2_reg"
+ ;;
+ @*)
+ eval "$2=mem"
+ _asm_memory "${1#@}" "$2_mem"
+ ;;
+ *)
+ eval "$2=imm"
+ _asm_eval "$1"
+ eval "$2_imm=\$_asm_value"
+ esac
+}
+
+_asm_op() {
+ case "$1" in
+ %*)
+ eval "$2=reg"
+ _asm_register "${1#%}" "$2_reg"
+ ;;
+ @*)
+ eval "$2=mem"
+ _asm_memory "${1#@}" "$2_mem"
+ ;;
+ *)
+ eval "$2=imm"
+ _asm_eval "$1"
+ eval "$2_imm=\$_asm_value"
+ esac
+}
+
+_asm_register8() {
+ case "$1" in
+ al) eval "$2=0" ;;
+ cl) eval "$2=1" ;;
+ dl) eval "$2=2" ;;
+ bl) eval "$2=3" ;;
+ ah) eval "$2=4" ;;
+ ch) eval "$2=5" ;;
+ dh) eval "$2=6" ;;
+ bh) eval "$2=7" ;;
+ *) _asm_panic "unknown register %$1"
+ esac
+}
+
+_asm_register() {
+ case "$1" in
+ eax) eval "$2=0" ;;
+ ecx) eval "$2=1" ;;
+ edx) eval "$2=2" ;;
+ ebx) eval "$2=3" ;;
+ esp) eval "$2=4" ;;
+ ebp) eval "$2=5" ;;
+ esi) eval "$2=6" ;;
+ edi) eval "$2=7" ;;
+ *) _asm_panic "unknown register %$1"
+ esac
+}
+
+_asm_memory() {
+ _asm_memory_base=
+ _asm_memory_index=
+ _asm_memory_scale=
+ _asm_memory_displ=
+ _asm_memory_arg="$1+"
+ while [ "$_asm_memory_arg" ] ; do
+ _asm_memory_head="${_asm_memory_arg%%+*}"
+ _asm_memory_arg="${_asm_memory_arg#*+}"
+ case "$_asm_memory_head" in
+ %*)
+ if [ -z "$_asm_memory_base" ] ; then
+ _asm_register "${_asm_memory_head#%}" _asm_memory_base
+ elif [ -z "$_asm_memory_index" ] ; then
+ _asm_register "${_asm_memory_head#%}" _asm_memory_index
+ _asm_memory_scale=0
+ else
+ _asm_panic "more than two registers in address calculation"
+ fi
+ ;;
+ [1248]%*)
+ if [ -z "$_asm_memory_index" ] ; then
+ _asm_register "${_asm_memory_head#[1248]%}" _asm_memory_index
+ _asm_memory_scale1=0
+ _asm_memory_scale2=1
+ _asm_memory_scale4=2
+ _asm_memory_scale8=3
+ : $(( _asm_memory_scale = _asm_memory_scale${_asm_memory_head%%\%*} ))
+ else
+ _asm_panic "two index registers in address calculation"
+ fi
+ ;;
+ *%*)
+ _asm_panic "illegal scale ${_asm_memory_head%%\%}"
+ ;;
+ *)
+ _asm_eval "$_asm_memory_head"
+ if [ -z "$_asm_memory_displ" ] ; then
+ _asm_memory_displ="$_asm_value"
+ else
+ _asm_panic "two displacements in address calculation"
+ fi
+ esac
+ done
+ [ "$_asm_memory_index" != 4 ] || _asm_panic "cannot use %esp as index"
+ for part in base index scale displ ; do
+ eval "$2_$part=\"\$_asm_memory_$part\""
+ done
+}
+
+_asm_eval() {
+ _asm_dropminus="${1#-}"
+ case "$_asm_dropminus" in
+ 0x*)
+ case "${_asm_dropminus#0x}" in
+ *[!0-9A-Fa-f]*)
+ ;;
+ *)
+ : $(( _asm_value = $1 ))
+ return
+ esac
+ ;;
+ 0*)
+ case "$_asm_dropminus" in
+ *[!0-7]*)
+ ;;
+ *)
+ : $(( _asm_value = $1 ))
+ return
+ esac
+ ;;
+ *[!0-9]*)
+ ;;
+ *)
+ : $(( _asm_value = $1 ))
+ return
+ esac
+ case "$1" in
+ [!a-zA-Z_]*|*[!a-zA-Z0-9_]*)
+ ;;
+ *)
+ eval "_asm_value=\$_asm_label_$1"
+ if [ "$_asm_value$_asm_pass0" = "" ] ; then
+ _asm_panic "label $1 not declared"
+ fi
+ : $(( _asm_value = _asm_value ))
+ return
+ esac
+ case "$1" in
+ *^|*.)
+ _asm_eval_L="${1%[.^]}"
+ _asm_L $_asm_eval_L
+ : $(( _asm_eval_L_idx = _asm_L_${_asm_eval_L}_idx ))
+ case "$1" in
+ *.) : $(( _asm_eval_L_idx += 1 ))
+ esac
+ eval "_asm_value=\$_asm_L_${_asm_eval_L}_${_asm_eval_L_idx}"
+ if [ "$_asm_value$_asm_pass0" = "" ] ; then
+ _asm_panic "label $1 not declared"
+ fi
+ : $(( _asm_value = _asm_value ))
+ return
+ esac
+ _asm_panic "failed to parse value $1"
+}
+
+_asm_panic() {
+ printf '%s\n' "$@" >&2
+ exit 1
+}
+
+_asm_printf() {
+ if [ $_asm_active != 0 ] ; then printf "$@" ; fi
+}
+
+_asm_start_elf=0
+_asm_mmap_min_addr=0x10000
+_asm_flags_text=0x1 # x
+_asm_flags_rodata=0x4 # r
+_asm_flags_data=0x6 # rw
+
+_asm_section() {
+ : $(( _asm_offset_$_asm_section = _asm_offset ))
+ _asm_section="$1"
+ : $(( _asm_offset = _asm_offset_$_asm_section ))
+ : $(( _asm_active = _asm_active_$_asm_section ))
+}
+
+text() { _asm_section text ; }
+rodata() { _asm_section rodata ; }
+data() { _asm_section data ; }
+bss() { _asm_section bss ; }
+
+_asm_set() {
+ : $(( _asm_set_new = ($2) ))
+ : $(( _asm_modified |= $1 != _asm_set_new ))
+ : $(( $1 = _asm_set_new ))
+}
+
+label() {
+ eval "_asm_label_oldval=\$_asm_label_$1"
+ if [ "$_asm_pass0" ] && [ "$_asm_label_oldval" ] ; then
+ _asm_panic "redefinition of label $1"
+ fi
+ if [ $# -eq 1 ]
+ then
+ _asm_set _asm_label_$1 _asm_offset
+ else
+ _asm_eval "$2"
+ _asm_set _asm_label_$1 '_asm_offset - _asm_value'
+ fi
+}
+
+_asm_L() {
+ [ $(( _asm_generation == _asm_L_$1_gen )) != 0 ] || {
+ : $(( _asm_L_$1_gen = _asm_generation ))
+ : $(( _asm_L_$1_idx = 0 ))
+ }
+}
+
+L() {
+ _asm_L $1
+ _asm_set _asm_L_$1_$((_asm_L_$1_idx += 1)) _asm_offset
+}
+
+_asm_elf() {
+ _asm_section elf
+ byte 0x7F # magic number
+ ascii 'ELF'
+ byte 1 # 32-bit format
+ byte 1 # little endian
+ byte 1 # ELF version
+ byte 3 # Linux
+ skip 8 # padding
+ word 2 # executable
+ word 3 # x86
+ long 1 # ELF version
+ long _start # entry point
+ long _asm_phoff # program header offset
+ long 0 # section header offset
+ long 0 # flags
+ word _asm_elfhdr_end # ELF header size
+ word 32 # program header entry size
+ word 4 # program header entry count
+ word 0 # section header entry size
+ word 0 # section header entry count
+ word 0 # section name string table index
+ label _asm_elfhdr_end
+ label _asm_phoff
+ for _asm_local_section in text rodata data ; do
+ : $(( _asm_local_section_length =
+ _asm_end_$_asm_local_section - _asm_start_$_asm_local_section ))
+ long 1 # PT_LOAD
+ long $(( _asm_foffset_$_asm_local_section )) # file offset
+ long $(( _asm_start_$_asm_local_section )) # virtual address
+ long 0 # physical address
+ long $_asm_local_section_length # size in file
+ if [ $_asm_local_section = data ]
+ then
+ long $(( _asm_end_bss - _asm_start_data ))
+ else
+ long $_asm_local_section_length # size in memory
+ fi
+ long $(( _asm_flags_$_asm_local_section )) # permission flags
+ long 0x1000 # alignment
+ done
+ # The PT_GNU_STACK segment is needed to avoid the kernel's
+ # read-implies-exec personality.
+ long 0x6474e551 # PT_GNU_STACK
+ long 0 # file offset
+ long 0 # virtual address
+ long 0 # physical address
+ long 0 # size in file
+ long 0 # size in memory
+ long 0x6 # read/write
+ long 0x1000 # alignment
+}
+
+_asm_run() {
+ for _asm_local_section in elf text rodata data bss ; do
+ : $(( _asm_offset_$_asm_local_section =
+ _asm_start_$_asm_local_section ))
+ : $(( _asm_active_$_asm_local_section = 0 ))
+ done
+ for _asm_local_section in "$@" ; do
+ : $(( _asm_active_$_asm_local_section = 1 ))
+ done
+ _asm_section=text
+ : $(( _asm_offset = _asm_offset_$_asm_section ))
+ : $(( _asm_active = _asm_active_$_asm_section ))
+
+ _asm_modified=0
+ : $(( _asm_generation += 1 ))
+ _asm_elf
+ text
+ prog
+
+ : $(( _asm_offset_$_asm_section = _asm_offset ))
+
+ _asm_set _asm_end_elf _asm_offset_elf
+ : $(( _asm_offset = _asm_end_elf + _asm_mmap_min_addr ))
+ for _asm_local_section in text rodata data bss ; do
+ : $(( _asm_offset2 = _asm_offset +
+ (_asm_offset_$_asm_local_section - _asm_start_$_asm_local_section) ))
+ _asm_set _asm_start_$_asm_local_section _asm_offset
+ _asm_set _asm_end_$_asm_local_section _asm_offset2
+ _asm_offset="$_asm_offset2"
+ [ "$_asm_local_section" = data ] || \
+ : $(( _asm_offset += 0x1000 ))
+ done
+
+ _asm_foffset=0
+ for _asm_local_section in elf text rodata data ; do
+ _asm_set _asm_foffset_$_asm_local_section _asm_foffset
+ : $(( _asm_foffset = _asm_foffset
+ + (_asm_end_$_asm_local_section - _asm_start_$_asm_local_section) ))
+ done
+}
+
+
+_asm_pass0=y
+_asm_run
+_asm_pass0=
+_asm_limit=20
+while [ $_asm_modified -eq 1 ] ; do
+ _asm_run
+ : $(( _asm_limit -= 1 ))
+ if [ $_asm_limit -eq 0 ] ; then
+ _asm_panic "pass limit exceeded, failed to converge"
+ fi
+done
+
+_asm_file_offset=0
+for _asm_top_section in elf text rodata data ; do
+ _asm_run $_asm_top_section
+done