https://github.com/akkartik/mu/blob/main/linux/105string-equal.subx
  1 # Comparing 'regular' size-prefixed strings.
  2 
  3 == code
  4 #   instruction                     effective address                                                   register    displacement    immediate
  5 # . op          subop               mod             rm32          base        index         scale       r32
  6 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  7 
  8 #? Entry:  # run all tests
  9 #? #?     e8/call test-compare-equal-strings/disp32
 10 #?     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 11 #?     # syscall_exit(Num-test-failures)
 12 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
 13 #?     e8/call  syscall_exit/disp32
 14 
 15 string-equal?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
 16     # pseudocode:
 17     #   if (s->size != benchmark->size) return false
 18     #   return string-starts-with?(s, benchmark)
 19     #
 20     # . prologue
 21     55/push-ebp
 22     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   . pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */

#define TAGS \
const char *tags[] = { "work", "net", "fnord", NULL };

#define DEFMODE			dotile /* dofloat */
#define DEFTAG			0 /* index */
#define FONT			"-*-terminus-medium-*-*-*-12-*-*-*-*-*-iso10646-*"
#define BGCOLOR			"#0d121d"
#define FGCOLOR			"#eeeeee"
#define BORDERCOLOR		"#3f484d"
#define MODKEY			Mod1Mask
#define NUMLOCKMASK		Mod2Mask
#define MASTERW			60 /* percent */

#define KEYS \
static Key key[] = { \
	/* modifier			key		function	arguments */ \
	{ MODKEY,			XK_1,		view,		{ .i = 0 } }, \
	{ MODKEY,			XK_2,		view,		{ .i = 1 } }, \
	{ MODKEY,			XK_3,		view,		{ .i = 2 } }, \
	{ MODKEY,			XK_j,		focusnext,	{ 0 } }, \
	{ MODKEY,			XK_k,		focusprev,	{ 0 } }, \
	{ MODKEY,			XK_m,		togglemax,	{ 0 } }, \
	{ MODKEY,			XK_p,		spawn, \
		{ .cmd = "exec `ls -lL /usr/bin /usr/local/bin 2>/dev/null | " \
			"awk 'NF>2 && $1 ~ /^[^d].*x/ {print $NF}' | sort | uniq | dmenu`" } }, \
	{ MODKEY,			XK_space,	togglemode,	{ 0 } }, \
	{ MODKEY,			XK_Return,	zoom,		{ 0 } }, \
	{ MODKEY|ControlMask,		XK_1,		toggleview,	{ .i = 0 } }, \
	{ MODKEY|ControlMask,		XK_2,		toggleview,	{ .i = 1 } }, \
	{ MODKEY|ControlMask,		XK_3,		toggleview,	{ .i = 2 } }, \
	{ MODKEY|ShiftMask,		XK_1,		replacetag,	{ .i = 0 } }, \
	{ MODKEY|ShiftMask,		XK_2,		replacetag,	{ .i = 1 } }, \
	{ MODKEY|ShiftMask,		XK_3,		replacetag,	{ .i = 2 } }, \
	{ MODKEY|ShiftMask,		XK_c,		killclient,	{ 0 } }, \
	{ MODKEY|ShiftMask,		XK_q,		quit,		{ 0 } }, \
	{ MODKEY|ShiftMask,		XK_Return,	spawn, \
		{ .cmd = "exec urxvt +sb -tr -bg black -fg '#eeeeee' -cr '#eeeeee' +sb " \
			"-fn '-*-terminus-medium-*-*-*-12-*-*-*-*-*-iso10646-*'" } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_1,		appendtag,	{ .i = 0 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_2,		appendtag,	{ .i = 1 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_3,		appendtag,	{ .i = 2 } }, \
};

#define RULES \
static Rule rule[] = { \
	/* class:instance regex		tags regex	isfloat */ \
	{ "Firefox.*",			"net",		False }, \
	{ "Gimp.*",			NULL,		True}, \
	{ "MPlayer.*",			NULL,		True}, \
	{ "Acroread.*",			NULL,		True}, \
};
1/r32/ecx . . # copy *edi to ecx 91 $string-starts-with?:sizes: 92 # if (s->size < bsize) return false 93 39/compare 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # compare *esi with ecx 94 7c/jump-if-< $string-starts-with?:false/disp8 95 # var currs/esi: (addr byte) = s->data 96 81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 4/imm32 # add to esi 97 # var currb/edi: (addr byte) = benchmark->data 98 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi 99 # var maxb/ecx: (addr byte) = &benchmark->data[benchmark->size] 100 01/add 3/mod/direct 1/rm32/ecx . . . 7/r32/edi . . # add edi to ecx 101 # var c1/eax: byte = 0 102 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 103 # var c2/edx: byte = 0 104 31/xor 3/mod/direct 2/rm32/edx . . . 2/r32/edx . . # clear edx 105 $string-starts-with?:loop: 106 # if (currs >= maxs) return true 107 39/compare 3/mod/direct 7/rm32/edi . . . 1/r32/ecx . . # compare edi with ecx 108 73/jump-if-addr>= $string-starts-with?:true/disp8 109 # c1 = *currs 110 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL 111 # c2 = *currb 112 8a/copy-byte 0/mod/indirect 7/rm32/edi . . . 2/r32/DL . . # copy byte at *edi to DL 113 # if (c1 != c2) return false 114 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx 115 75/jump-if-!= $string-starts-with?:false/disp8 116 # ++currs 117 46/increment-esi 118 # ++currb 119 47/increment-edi 120 eb/jump $string-starts-with?:loop/disp8 121 $string-starts-with?:true: 122 b8/copy-to-eax 1/imm32 123 eb/jump $string-starts-with?:end/disp8 124 $string-starts-with?:false: 125 b8/copy-to-eax 0/imm32 126 $string-starts-with?:end: 127 # . restore registers 128 5f/pop-to-edi 129 5e/pop-to-esi 130 5a/pop-to-edx 131 59/pop-to-ecx 132 # . epilogue 133 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 134 5d/pop-to-ebp 135 c3/return 136 137 # - tests 138 139 test-compare-empty-with-empty-string: 140 # eax = string-equal?("", "") 141 # . . push args 142 68/push ""/imm32 143 68/push ""/imm32 144 # . . call 145 e8/call string-equal?/disp32 146 # . . discard args 147 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 148 # check-ints-equal(eax, 1, msg) 149 # . . push args 150 68/push "F - test-compare-empty-with-empty-string"/imm32 151 68/push 1/imm32/true 152 50/push-eax 153 # . . call 154 e8/call check-ints-equal/disp32 155 # . . discard args 156 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 157 c3/return 158 159 test-compare-empty-with-non-empty-string: # also checks size-mismatch code path 160 # eax = string-equal?("", "Abc") 161 # . . push args 162 68/push "Abc"/imm32 163 68/push ""/imm32 164 # . . call 165 e8/call string-equal?/disp32 166 # . . discard args 167 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 168 # check-ints-equal(eax, 0, msg) 169 # . . push args 170 68/push "F - test-compare-empty-with-non-empty-string"/imm32 171 68/push 0/imm32/false 172 50/push-eax 173 # . . call 174 e8/call check-ints-equal/disp32 175 # . . discard args 176 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 177 c3/return 178 179 test-compare-equal-strings: 180 # eax = string-equal?("Abc", "Abc") 181 # . . push args 182 68/push "Abc"/imm32 183 68/push "Abc"/imm32 184 # . . call 185 e8/call string-equal?/disp32 186 # . . discard args 187 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 188 # check-ints-equal(eax, 1, msg) 189 # . . push args 190 68/push "F - test-compare-equal-strings"/imm32 191 68/push 1/imm32/true 192 50/push-eax 193 # . . call 194 e8/call check-ints-equal/disp32 195 # . . discard args 196 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 197 c3/return 198 199 test-compare-inequal-strings-equal-sizes: 200 # eax = string-equal?("Abc", "Adc") 201 # . . push args 202 68/push "Adc"/imm32 203 68/push "Abc"/imm32 204 # . . call 205 e8/call string-equal?/disp32 206 # . . discard args 207 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 208 # check-ints-equal(eax, 0, msg) 209 # . . push args 210 68/push "F - test-compare-inequal-strings-equal-sizes"/imm32 211 68/push 0/imm32/false 212 50/push-eax 213 # . . call 214 e8/call check-ints-equal/disp32 215 # . . discard args 216 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 217 c3/return 218 219 # helper for later tests 220 check-strings-equal: # s: (addr array byte), expected: (addr array byte), msg: (addr array byte) 221 # . prologue 222 55/push-ebp 223 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 224 # . save registers 225 50/push-eax 226 # var eax: boolean = string-equal?(s, expected) 227 # . . push args 228 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 229 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 230 # . . call 231 e8/call string-equal?/disp32 232 # . . discard args 233 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 234 # check-ints-equal(eax, 1, msg) 235 # . . push args 236 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 237 68/push 1/imm32 238 50/push-eax 239 # . . call 240 e8/call check-ints-equal/disp32 241 # . . discard args 242 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 243 $check-strings-equal:end: 244 # . restore registers 245 58/pop-to-eax 246 # . epilogue 247 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 248 5d/pop-to-ebp 249 c3/return 250 251 # test the helper 252 test-check-strings-equal: 253 # check-strings-equal("Abc", "Abc") 254 # . . push args 255 68/push "Abc"/imm32 256 68/push "Abc"/imm32 257 # . . call 258 e8/call check-strings-equal/disp32 259 # . . discard args 260 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 261 c3/return 262 263 # . . vim:nowrap:textwidth=0