# Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas # which corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt # except that we support hex numbers of multiple digits. # # To run: # $ ./bootstrap translate init.linux 0*.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b # $ echo '1a' |./bootstrap run apps/crenshaw2-1b # Expected output: # # syscall(exit, 1a) # bb/copy-to-ebx 3/imm32 # b8/copy-to-eax 1/imm32/exit # cd/syscall 0x80/imm8 # # To run the generated output: # $ echo '1a' |./bootstrap run apps/crenshaw2-1b > z1.subx # $ ./bootstrap translate init.linux z1.subx -o z1 # $ ./bootstrap run z1 # $ echo $? # 26 # 0x1a in decimal # # Stdin must contain just a single hex digit. Other input will print an error: # $ echo 'xyz' |./bootstrap run apps/crenshaw2-1b # Error: integer expected # # Names in this file sometimes follow Crenshaw's original rather than my usual # naming conventions. == code # instruction effective address register displacement immediate # . op subop mod rm32 base index scale r32 # . 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 Entry: # run tests if necessary, call 'compile' if not # . prologue 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # initialize heap # . Heap = new-segment(Heap-size) # . . push args 68/push Heap/imm32 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size # . . call e8/call new-segment/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # - if argc > 1 and argv[1] == "test", then return run_tests() # if (argc <= 1) goto run-main 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp 7e/jump-if-<= $run-main/disp8 # if (!kernel-string-equal?(argv[1], "test")) goto run-main # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call kernel-string-equal?/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . if (eax == false) goto run-main 3d/compare-eax-and 0/imm32/false 74/jump-if-= $run-main/disp8 # run-tests() e8/call run-tests/disp32 # syscall(exit, *Num-test-failures) 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx eb/jump $main:end/disp8 $run-main: # - otherwise read a program from stdin and emit its translation to stdout # . compile(Stdin, 1/stdout, 2/stderr, 0) # . . push args 68/push 0/imm32/exit-descriptor 68/push 2/imm32/stderr 68/push 1/imm32/stdout 68/push Stdin/imm32 # . . call e8/call compile/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp # syscall(exit, 0) bb/copy-to-ebx 0/imm32 $main:end: e8/call syscall_exit/disp32 # the main entry point compile: # in: (addr buffered-file), out: fd or (addr stream byte), err: fd or (addr stream byte), ed: (addr exit-descriptor) # . prologue 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 50/push-eax 51/push-ecx # prime the pump # . Look = get-char(in) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call get-char/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # var num/ecx: (stream byte 7) # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. # Add 12 bytes for 'read', 'write' and 'size' fields, for a total of 19 bytes, or 0x13 in hex. # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x13/imm32 # subtract from esp 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx # initialize the stream # . num->size = 7 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ecx . . . . 8/disp8 7/imm32 # copy to *(ecx+8) # . clear-stream(num) # . . push args 51/push-ecx # . . call e8/call clear-stream/disp32 # . . discard ar
/* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */

#define BORDERPX		1
#define FONT			"-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*"
#define NORMBORDERCOLOR		"#dddddd"
#define NORMBGCOLOR		"#eeeeee"
#define NORMFGCOLOR		"#222222"
#define SELBORDERCOLOR		"#ff0000"
#define SELBGCOLOR		"#006699"
#define SELFGCOLOR		"#ffffff"

#define MASTER			600		/* per thousand */
#define MODKEY			Mod1Mask
#define NMASTER			1		/* clients in master area */
#define SNAP			20		/* pixel */
#define TOPBAR			True		/* False */

#define TAGS \
const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL };

#define LAYOUTS \
Layout layout[] = { \
	/* symbol		function */ \
	{ "[]=",		tile }, /* first entry is default */ \
	{ "><>",		versatile }, \
};

#define KEYS \
static Key key[] = { \
	/* modifier			key		function	argument */ \
	{ MODKEY|ShiftMask,		XK_Return,	spawn,		{ .cmd = "exec xterm" } }, \
	{ MODKEY,			XK_Tab,		focusnext,	{ 0 } }, \
	{ MODKEY|ShiftMask,		XK_Tab,		focusprev,	{ 0 } }, \
	{ MODKEY,			XK_Return,	zoom,		{ 0 } }, \
	{ MODKEY,			XK_g,		resizemaster,	{ .i = 15 } }, \
	{ MODKEY,			XK_s,		resizemaster,	{ .i = -15 } }, \
	{ MODKEY,			XK_i,		incnmaster,	{ .i = 1 } }, \
	{ MODKEY,			XK_d,		incnmaster,	{ .i = -1 } }, \
	{ MODKEY|ShiftMask,		XK_0,		tag,		{ .i = -1 } }, \
	{ MODKEY|ShiftMask,		XK_1,		tag,		{ .i = 0 } }, \
	{ MODKEY|ShiftMask,		XK_2,		tag,		{ .i = 1 } }, \
	{ MODKEY|ShiftMask,		XK_3,		tag,		{ .i = 2 } }, \
	{ MODKEY|ShiftMask,		XK_4,		tag,		{ .i = 3 } }, \
	{ MODKEY|ShiftMask,		XK_5,		tag,		{ .i = 4 } }, \
	{ MODKEY|ShiftMask,		XK_6,		tag,		{ .i = 5 } }, \
	{ MODKEY|ShiftMask,		XK_7,		tag,		{ .i = 6 } }, \
	{ MODKEY|ShiftMask,		XK_8,		tag,		{ .i = 7 } }, \
	{ MODKEY|ShiftMask,		XK_9,		tag,		{ .i = 8 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_1,		toggletag,	{ .i = 0 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_2,		toggletag,	{ .i = 1 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_3,		toggletag,	{ .i = 2 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_4,		toggletag,	{ .i = 3 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_5,		toggletag,	{ .i = 4 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_6,		toggletag,	{ .i = 5 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_7,		toggletag,	{ .i = 6 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_8,		toggletag,	{ .i = 7 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_9,		toggletag,	{ .i = 8 } }, \
	{ MODKEY|ShiftMask,		XK_c,		killclient,	{ 0 } }, \
	{ MODKEY,			XK_space,	togglelayout,	{ 0 } }, \
	{ MODKEY|ShiftMask,		XK_space,	toggleversatile	{ 0 } }, \
	{ MODKEY,			XK_0,		view,		{ .i = -1 } }, \
	{ MODKEY,			XK_1,		view,		{ .i = 0 } }, \
	{ MODKEY,			XK_2,		view,		{ .i = 1 } }, \
	{ MODKEY,			XK_3,		view,		{ .i = 2 } }, \
	{ MODKEY,			XK_4,		view,		{ .i = 3 } }, \
	{ MODKEY,			XK_5,		view,		{ .i = 4 } }, \
	{ MODKEY,			XK_6,		view,		{ .i = 5 } }, \
	{ MODKEY,			XK_7,		view,		{ .i = 6 } }, \
	{ MODKEY,			XK_8,		view,		{ .i = 7 } }, \
	{ MODKEY,			XK_9,		view,		{ .i = 8 } }, \
	{ MODKEY|ControlMask,		XK_1,		toggleview,	{ .i = 0 } }, \
	{ MODKEY|ControlMask,		XK_2,		toggleview,	{ .i = 1 } }, \
	{ MODKEY|ControlMask,		XK_3,		toggleview,	{ .i = 2 } }, \
	{ MODKEY|ControlMask,		XK_4,		toggleview,	{ .i = 3 } }, \
	{ MODKEY|ControlMask,		XK_5,		toggleview,	{ .i = 4 } }, \
	{ MODKEY|ControlMask,		XK_6,		toggleview,	{ .i = 5 } }, \
	{ MODKEY|ControlMask,		XK_7,		toggleview,	{ .i = 6 } }, \
	{ MODKEY|ControlMask,		XK_8,		toggleview,	{ .i = 7 } }, \
	{ MODKEY|ControlMask,		XK_9,		toggleview,	{ .i = 8 } }, \
	{ MODKEY|ShiftMask,		XK_q,		quit,		{ 0 } }, \
};

/* Query class:instance:title for regex matching info with following command:
 * xprop | awk -F '"' '/^WM_CLASS/ { printf("%s:%s:",$4,$2) }; /^WM_NAME/ { printf("%s\n",$2) }' */
#define RULES \
static Rule rule[] = { \
	/* class:instance:title regex	tags regex	versatile */ \
	{ "Gimp",			NULL,		True }, \
	{ "MPlayer",			NULL,		True }, \
	{ "Acroread",			NULL,		True }, \
};
ll e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # initialize 'in' # . write(_test-stream, "3") # . . push args 68/push "3"/imm32 68/push _test-stream/imm32 # . . call e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # initialize exit-descriptor 'ed' for the call to 'get-num' below # . var ed/eax: exit-descriptor 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax # . tailor-exit-descriptor(ed, 16) # . . push args 68/push 0x10/imm32/nbytes-of-args-for-get-num 50/push-eax/ed # . . call e8/call tailor-exit-descriptor/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # prime the pump # . get-char(_test-buffered-file) # . . push args 68/push _test-buffered-file/imm32 # . . call e8/call get-char/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # get-num(in, out, err, ed) # . . push args 50/push-eax/ed 68/push _test-error-stream/imm32 68/push _test-output-stream/imm32 68/push _test-buffered-file/imm32 # . . call e8/call get-num/disp32 # registers except esp may be clobbered at this point # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp # check-ints-equal(*_test-output-stream->data, '3', msg) # . . push args 68/push "F - test-get-num-reads-single-digit"/imm32 68/push 0x33/imm32 b8/copy-to-eax _test-output-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12) # . . call e8/call check-ints-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 5d/pop-to-ebp c3/return test-get-num-aborts-on-non-digit-in-Look: # - check that get-num returns first character if it's a digit # This test uses exit-descriptors. Use ebp for setting up local variables. 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # clear all streams # . clear-stream(_test-stream) # . . push args 68/push _test-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream($_test-buffered-file->buffer) # . . push args 68/push $_test-buffered-file->buffer/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream(_test-output-stream) # . . push args 68/push _test-output-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream(_test-error-stream) # . . push args 68/push _test-error-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # initialize 'in' # . write(_test-stream, "3") # . . push args 68/push "3"/imm32 68/push _test-stream/imm32 # . . call e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # initialize exit-descriptor 'ed' for the call to 'get-num' below # . var ed/eax: exit-descriptor 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax # . tailor-exit-descriptor(ed, 16) # . . push args 68/push 0x10/imm32/nbytes-of-args-for-get-num 50/push-eax/ed # . . call e8/call tailor-exit-descriptor/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # *don't* prime the pump # get-num(in, out, err, ed) # . . push args 50/push-eax/ed 68/push _test-error-stream/imm32 68/push _test-output-stream/imm32 68/push _test-buffered-file/imm32 # . . call e8/call get-num/disp32 # registers except esp may be clobbered at this point # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp # check that get-num tried to call exit(1) # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 # . . push args 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 68/push 2/imm32 # . . push ed->value ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . call e8/call check-ints-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 5d/pop-to-ebp c3/return test-get-num-reads-multiple-digits: # - check that get-num returns all initial digits until it encounters a non-digit # This test uses exit-descriptors. Use ebp for setting up local variables. 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # clear all streams # . clear-stream(_test-stream) # . . push args 68/push _test-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream($_test-buffered-file->buffer) # . . push args 68/push $_test-buffered-file->buffer/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream(_test-output-stream) # . . push args 68/push _test-output-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream(_test-error-stream) # . . push args 68/push _test-error-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # initialize 'in' # . write(_test-stream, "3456 x") # . . push args 68/push "3456"/imm32 68/push _test-stream/imm32 # . . call e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # initialize exit-descriptor 'ed' for the call to 'get-num' below # . var ed/eax: exit-descriptor 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax # . tailor-exit-descriptor(ed, 16) # . . push args 68/push 0x10/imm32/nbytes-of-args-for-get-num 50/push-eax/ed # . . call e8/call tailor-exit-descriptor/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # prime the pump # . get-char(_test-buffered-file) # . . push args 68/push _test-buffered-file/imm32 # . . call e8/call get-char/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # get-num(in, out, err, ed) # . . push args 50/push-eax/ed 68/push _test-error-stream/imm32 68/push _test-output-stream/imm32 68/push _test-buffered-file/imm32 # . . call e8/call get-num/disp32 # registers except esp may be clobbered at this point # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp # check-ints-equal(*_test-output-stream->data, '3456', msg) # . . push args 68/push "F - test-get-num-reads-multiple-digits"/imm32 68/push 0x36353433/imm32 b8/copy-to-eax _test-output-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12) # . . call e8/call check-ints-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 5d/pop-to-ebp c3/return test-get-num-reads-multiple-digits-followed-by-nondigit: # - check that get-num returns all initial digits until it encounters a non-digit # This test uses exit-descriptors. Use ebp for setting up local variables. 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # clear all streams # . clear-stream(_test-stream) # . . push args 68/push _test-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream($_test-buffered-file->buffer) # . . push args 68/push $_test-buffered-file->buffer/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream(_test-output-stream) # . . push args 68/push _test-output-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . clear-stream(_test-error-stream) # . . push args 68/push _test-error-stream/imm32 # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # initialize 'in' # . write(_test-stream, "3456 x") # . . push args 68/push "3456 x"/imm32 68/push _test-stream/imm32 # . . call e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # initialize exit-descriptor 'ed' for the call to 'get-num' below # . var ed/eax: exit-descriptor 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax # . tailor-exit-descriptor(ed, 16) # . . push args 68/push 0x10/imm32/nbytes-of-args-for-get-num 50/push-eax/ed # . . call e8/call tailor-exit-descriptor/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # prime the pump # . get-char(_test-buffered-file) # . . push args 68/push _test-buffered-file/imm32 # . . call e8/call get-char/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # get-num(in, out, err, ed) # . . push args 50/push-eax/ed 68/push _test-error-stream/imm32 68/push _test-output-stream/imm32 68/push _test-buffered-file/imm32 # . . call e8/call get-num/disp32 # registers except esp may be clobbered at this point # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp # check-ints-equal(*_test-output-stream->data, '3456', msg) # . . push args 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 68/push 0x36353433/imm32 b8/copy-to-eax _test-output-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12) # . . call e8/call check-ints-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 5d/pop-to-ebp c3/return ## helpers # write(f, "Error: "+s+" expected\n") then stop(ed, 1) expected: # ed: (addr exit-descriptor), f: fd or (addr stream byte), s: (addr array byte) # . prologue 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # write(f, "Error: ") # . . push args 68/push "Error: "/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # write(f, s) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # write(f, " expected\n") # . . push args 68/push " expected\n"/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # stop(ed, 1) # . . push args 68/push 1/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call stop/disp32 # should never get past this point $expected:dead-end: # . epilogue 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return # read a byte from 'f', and save it in 'Look' get-char: # f: (addr buffered-file) # . prologue 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 50/push-eax # eax = read-byte-buffered(f) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call read-byte-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # save eax to Look 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Look/disp32 . # copy eax to *Look $get-char:end: # . restore registers 58/pop-to-eax # . epilogue 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return is-digit?: # c: int -> eax: boolean # . prologue 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # eax = false b8/copy-to-eax 0/imm32 # if (c < '0') return false 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x30/imm32 # compare *(ebp+8) 7c/jump-if-< $is-digit?:end/disp8 # if (c > '9') return false 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x39/imm32 # compare *(ebp+8) 7f/jump-if-> $is-digit?:end/disp8 # otherwise return true b8/copy-to-eax 1/imm32 $is-digit?:end: # . epilogue 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return == data Look: # (char with some extra padding) 0/imm32 # . . vim:nowrap:textwidth=0