From ce2c1efc41470764126e9a1a7f4e0cfec4213587 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 14 Jul 2019 09:42:36 -0700 Subject: [PATCH] . --- html/subx/000organization.cc.html | 10 +- html/subx/001help.cc.html | 10 +- html/subx/002test.cc.html | 12 +- html/subx/003trace.cc.html | 16 +- html/subx/003trace.test.cc.html | 10 +- html/subx/010---vm.cc.html | 415 +- html/subx/011run.cc.html | 28 +- html/subx/012elf.cc.html | 34 +- html/subx/013direct_addressing.cc.html | 473 +- html/subx/014indirect_addressing.cc.html | 2007 +-- html/subx/015immediate_addressing.cc.html | 114 +- html/subx/016index_addressing.cc.html | 18 +- html/subx/017jump_disp8.cc.html | 128 +- html/subx/018jump_disp32.cc.html | 522 +- html/subx/019functions.cc.html | 28 +- html/subx/020syscalls.cc.html | 48 +- html/subx/021byte_addressing.cc.html | 34 +- html/subx/022div.cc.html | 16 +- html/subx/028translate.cc.html | 14 +- html/subx/029transforms.cc.html | 8 +- html/subx/030---operands.cc.html | 14 +- html/subx/031check_operands.cc.html | 943 +- html/subx/032check_operand_bounds.cc.html | 16 +- html/subx/034compute_segment_address.cc.html | 14 +- html/subx/035labels.cc.html | 16 +- html/subx/036global_variables.cc.html | 16 +- html/subx/038---literal_strings.cc.html | 12 +- html/subx/039debug.cc.html | 118 +- html/subx/040---tests.cc.html | 8 +- html/subx/049memory_layout.subx.html | 6 +- html/subx/050_write.subx.html | 12 +- html/subx/051test.subx.html | 10 +- html/subx/052kernel-string-equal.subx.html | 24 +- html/subx/053new-segment.subx.html | 14 +- html/subx/054string-equal.subx.html | 402 +- html/subx/055stream.subx.html | 14 +- html/subx/056trace.subx.html | 1399 ++- html/subx/057write.subx.html | 18 +- html/subx/058stream-equal.subx.html | 1186 +- html/subx/059stop.subx.html | 357 +- html/subx/060read.subx.html | 32 +- html/subx/061read-byte.subx.html | 539 +- html/subx/062write-stream.subx.html | 26 +- html/subx/063error.subx.html | 14 +- html/subx/064write-byte.subx.html | 42 +- html/subx/065hex.subx.html | 1452 +-- html/subx/066write-buffered.subx.html | 415 +- html/subx/067print-int.subx.html | 743 +- html/subx/068error-byte.subx.html | 30 +- html/subx/069allocate.subx.html | 20 +- html/subx/070new-stream.subx.html | 14 +- html/subx/071read-line.subx.html | 428 +- html/subx/072slice.subx.html | 2119 ++-- html/subx/073next-token.subx.html | 1814 ++- html/subx/074print-int-decimal.subx.html | 545 +- html/subx/075array-equal.subx.html | 694 ++ html/subx/076zero-out.subx.html | 147 + html/subx/apps/assort.subx.html | 1868 ++- html/subx/apps/crenshaw2-1.subx.html | 1116 +- html/subx/apps/crenshaw2-1b.subx.html | 1516 +-- html/subx/apps/dquotes.subx.html | 4828 ++++---- html/subx/apps/factorial.subx.html | 204 +- html/subx/apps/handle.subx.html | 24 +- html/subx/apps/hex.subx.html | 2997 ++--- html/subx/apps/pack.subx.html | 10798 +++++++---------- html/subx/apps/subx-common.subx.html | 3243 ++++- html/subx/apps/survey.subx.html | 3188 +++++ 67 files changed, 26591 insertions(+), 20809 deletions(-) create mode 100644 html/subx/075array-equal.subx.html create mode 100644 html/subx/076zero-out.subx.html create mode 100644 html/subx/apps/survey.subx.html diff --git a/html/subx/000organization.cc.html b/html/subx/000organization.cc.html index d586451d..87c51792 100644 --- a/html/subx/000organization.cc.html +++ b/html/subx/000organization.cc.html @@ -3,8 +3,8 @@ Mu - subx/000organization.cc - - + + @@ -14,12 +14,12 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.LineNr { } .Delimiter { color: #c000c0; } +.LineNr { } +.Comment { color: #005faf; } .Constant { color: #008787; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } .PreProc { color: #c000c0; } --> @@ -37,7 +37,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/001help.cc.html b/html/subx/001help.cc.html index 85db4952..a84f7b27 100644 --- a/html/subx/001help.cc.html +++ b/html/subx/001help.cc.html @@ -3,8 +3,8 @@ Mu - subx/001help.cc - - + + @@ -15,14 +15,14 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .CommentedCode { color: #8a8a8a; } -.PreProc { color: #c000c0; } .LineNr { } .Constant { color: #008787; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.PreProc { color: #c000c0; } .cSpecial { color: #008000; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/002test.cc.html b/html/subx/002test.cc.html index e022df44..266a34ed 100644 --- a/html/subx/002test.cc.html +++ b/html/subx/002test.cc.html @@ -3,8 +3,8 @@ Mu - subx/002test.cc - - + + @@ -14,16 +14,16 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.cSpecial { color: #008000; } +.CommentedCode { color: #8a8a8a; } .LineNr { } .Constant { color: #008787; } -.CommentedCode { color: #8a8a8a; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } .PreProc { color: #c000c0; } +.cSpecial { color: #008000; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/003trace.cc.html b/html/subx/003trace.cc.html index 03d36a46..6b45ab09 100644 --- a/html/subx/003trace.cc.html +++ b/html/subx/003trace.cc.html @@ -3,8 +3,8 @@ Mu - subx/003trace.cc - - + + @@ -14,15 +14,15 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.cSpecial { color: #008000; } -.PreProc { color: #c000c0; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.cSpecial { color: #008000; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } +.PreProc { color: #c000c0; } .SalientComment { color: #0000af; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/003trace.test.cc.html b/html/subx/003trace.test.cc.html index e55463b3..784b88db 100644 --- a/html/subx/003trace.test.cc.html +++ b/html/subx/003trace.test.cc.html @@ -3,8 +3,8 @@ Mu - subx/003trace.test.cc - - + + @@ -16,9 +16,9 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } .Constant { color: #008787; } -.Delimiter { color: #c000c0; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Comment { color: #005faf; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Delimiter { color: #c000c0; } --> @@ -35,7 +35,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/010---vm.cc.html b/html/subx/010---vm.cc.html index ff722e43..5dcabeb4 100644 --- a/html/subx/010---vm.cc.html +++ b/html/subx/010---vm.cc.html @@ -3,8 +3,8 @@ Mu - subx/010---vm.cc - - + + @@ -14,15 +14,15 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.cSpecial { color: #008000; } .PreProc { color: #c000c0; } .LineNr { } .Constant { color: #008787; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.cSpecial { color: #008000; } .SalientComment { color: #0000af; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -245,7 +245,7 @@ if ('onhashchange' in window) { 184 return static_cast<int8_t>(read_mem_u8(addr)); 185 } 186 inline uint32_t read_mem_u32(uint32_t addr) { -187 uint32_t* handle = mem_addr_u32(addr); // error messages get printed here +187 uint32_t* handle = mem_addr_u32(addr); // error messages get printed here 188 return handle ? *handle : 0; 189 } 190 inline int32_t read_mem_i32(uint32_t addr) { @@ -257,224 +257,219 @@ if ('onhashchange' in window) { 196 for (int i = 0; i < SIZE(Mem); ++i) { 197 if (Mem.at(i).match(addr)) { 198 if (result) -199 raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); +199 raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); 200 result = &Mem.at(i).data(addr); 201 } 202 } 203 if (result == NULL) { 204 if (Trace_file) Trace_file.flush(); -205 raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end(); -206 } -207 return result; -208 } -209 inline int8_t* mem_addr_i8(uint32_t addr) { -210 return reinterpret_cast<int8_t*>(mem_addr_u8(addr)); -211 } -212 inline uint32_t* mem_addr_u32(uint32_t addr) { -213 uint32_t* result = NULL; -214 for (int i = 0; i < SIZE(Mem); ++i) { -215 if (Mem.at(i).match32(addr)) { -216 if (result) -217 raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); -218 result = reinterpret_cast<uint32_t*>(&Mem.at(i).data(addr)); -219 } -220 } -221 if (result == NULL) { -222 if (Trace_file) Trace_file.flush(); -223 raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end(); -224 raise << "The entire 4-byte word should be initialized and lie in a single segment.\n" << end(); -225 } -226 return result; -227 } -228 inline int32_t* mem_addr_i32(uint32_t addr) { -229 return reinterpret_cast<int32_t*>(mem_addr_u32(addr)); -230 } -231 // helper for some syscalls. But read-only. -232 inline const char* mem_addr_kernel_string(uint32_t addr) { -233 return reinterpret_cast<const char*>(mem_addr_u8(addr)); -234 } -235 inline string mem_addr_string(uint32_t addr, uint32_t size) { -236 ostringstream out; -237 for (size_t i = 0; i < size; ++i) -238 out << read_mem_u8(addr+i); -239 return out.str(); -240 } -241 +205 raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end(); +206 exit(1); +207 } +208 return result; +209 } +210 inline int8_t* mem_addr_i8(uint32_t addr) { +211 return reinterpret_cast<int8_t*>(mem_addr_u8(addr)); +212 } +213 inline uint32_t* mem_addr_u32(uint32_t addr) { +214 uint32_t* result = NULL; +215 for (int i = 0; i < SIZE(Mem); ++i) { +216 if (Mem.at(i).match32(addr)) { +217 if (result) +218 raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); +219 result = reinterpret_cast<uint32_t*>(&Mem.at(i).data(addr)); +220 } +221 } +222 if (result == NULL) { +223 if (Trace_file) Trace_file.flush(); +224 raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end(); +225 raise << "The entire 4-byte word should be initialized and lie in a single segment.\n" << end(); +226 } +227 return result; +228 } +229 inline int32_t* mem_addr_i32(uint32_t addr) { +230 return reinterpret_cast<int32_t*>(mem_addr_u32(addr)); +231 } +232 // helper for some syscalls. But read-only. +233 inline const char* mem_addr_kernel_string(uint32_t addr) { +234 return reinterpret_cast<const char*>(mem_addr_u8(addr)); +235 } +236 inline string mem_addr_string(uint32_t addr, uint32_t size) { +237 ostringstream out; +238 for (size_t i = 0; i < size; ++i) +239 out << read_mem_u8(addr+i); +240 return out.str(); +241 } 242 -243 inline void write_mem_u8(uint32_t addr, uint8_t val) { -244 uint8_t* handle = mem_addr_u8(addr); -245 if (handle != NULL) *handle = val; -246 } -247 inline void write_mem_i8(uint32_t addr, int8_t val) { -248 int8_t* handle = mem_addr_i8(addr); -249 if (handle != NULL) *handle = val; -250 } -251 inline void write_mem_u32(uint32_t addr, uint32_t val) { -252 uint32_t* handle = mem_addr_u32(addr); -253 if (handle != NULL) *handle = val; -254 } -255 inline void write_mem_i32(uint32_t addr, int32_t val) { -256 int32_t* handle = mem_addr_i32(addr); -257 if (handle != NULL) *handle = val; -258 } -259 -260 inline bool already_allocated(uint32_t addr) { -261 bool result = false; -262 for (int i = 0; i < SIZE(Mem); ++i) { -263 if (Mem.at(i).match(addr)) { -264 if (result) -265 raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); -266 result = true; -267 } -268 } -269 return result; -270 } -271 -272 //:: core interpreter loop -273 -274 :(code) -275 // skeleton of how x86 instructions are decoded -276 void run_one_instruction() { -277 uint8_t op=0, op2=0, op3=0; -278 // Run One Instruction -279 if (Trace_file) { -280 dump_registers(); -281 // End Dump Info for Instruction -282 } -283 uint32_t inst_start_address = EIP; -284 op = next(); -285 trace(Callstack_depth+1, "run") << "0x" << HEXWORD << inst_start_address << " opcode: " << HEXBYTE << NUM(op) << end(); -286 switch (op) { -287 case 0xf4: // hlt -288 EIP = End_of_program; -289 break; -290 // End Single-Byte Opcodes -291 case 0x0f: -292 switch(op2 = next()) { -293 // End Two-Byte Opcodes Starting With 0f -294 default: -295 cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n'; -296 DUMP(""); +243 +244 inline void write_mem_u8(uint32_t addr, uint8_t val) { +245 uint8_t* handle = mem_addr_u8(addr); +246 if (handle != NULL) *handle = val; +247 } +248 inline void write_mem_i8(uint32_t addr, int8_t val) { +249 int8_t* handle = mem_addr_i8(addr); +250 if (handle != NULL) *handle = val; +251 } +252 inline void write_mem_u32(uint32_t addr, uint32_t val) { +253 uint32_t* handle = mem_addr_u32(addr); +254 if (handle != NULL) *handle = val; +255 } +256 inline void write_mem_i32(uint32_t addr, int32_t val) { +257 int32_t* handle = mem_addr_i32(addr); +258 if (handle != NULL) *handle = val; +259 } +260 +261 inline bool already_allocated(uint32_t addr) { +262 bool result = false; +263 for (int i = 0; i < SIZE(Mem); ++i) { +264 if (Mem.at(i).match(addr)) { +265 if (result) +266 raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); +267 result = true; +268 } +269 } +270 return result; +271 } +272 +273 //:: core interpreter loop +274 +275 :(code) +276 // skeleton of how x86 instructions are decoded +277 void run_one_instruction() { +278 uint8_t op=0, op2=0, op3=0; +279 // Run One Instruction +280 if (Trace_file) { +281 dump_registers(); +282 // End Dump Info for Instruction +283 } +284 uint32_t inst_start_address = EIP; +285 op = next(); +286 trace(Callstack_depth+1, "run") << "0x" << HEXWORD << inst_start_address << " opcode: " << HEXBYTE << NUM(op) << end(); +287 switch (op) { +288 case 0xf4: // hlt +289 EIP = End_of_program; +290 break; +291 // End Single-Byte Opcodes +292 case 0x0f: +293 switch(op2 = next()) { +294 // End Two-Byte Opcodes Starting With 0f +295 default: +296 cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n'; 297 exit(1); 298 } 299 break; 300 case 0xf2: -301 switch(op2 = next()) { +301 switch(op2 = next()) { 302 // End Two-Byte Opcodes Starting With f2 303 case 0x0f: -304 switch(op3 = next()) { +304 switch(op3 = next()) { 305 // End Three-Byte Opcodes Starting With f2 0f 306 default: -307 cerr << "unrecognized third opcode after f2 0f: " << HEXBYTE << NUM(op3) << '\n'; -308 DUMP(""); -309 exit(1); -310 } -311 break; -312 default: -313 cerr << "unrecognized second opcode after f2: " << HEXBYTE << NUM(op2) << '\n'; -314 DUMP(""); -315 exit(1); -316 } -317 break; -318 case 0xf3: -319 switch(op2 = next()) { -320 // End Two-Byte Opcodes Starting With f3 -321 case 0x0f: -322 switch(op3 = next()) { -323 // End Three-Byte Opcodes Starting With f3 0f -324 default: -325 cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n'; -326 DUMP(""); -327 exit(1); -328 } -329 break; -330 default: -331 cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n'; -332 DUMP(""); -333 exit(1); -334 } -335 break; -336 default: -337 cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n'; -338 DUMP(""); -339 exit(1); -340 } -341 } -342 -343 inline uint8_t next() { -344 return read_mem_u8(EIP++); -345 } -346 -347 void dump_registers() { -348 ostringstream out; -349 out << "registers before: "; -350 for (int i = 0; i < NUM_INT_REGISTERS; ++i) { -351 if (i > 0) out << "; "; -352 out << " " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u; -353 } -354 out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF; -355 trace(Callstack_depth+1, "run") << out.str() << end(); -356 } -357 -358 //: start tracking supported opcodes -359 :(before "End Globals") -360 map</*op*/string, string> Name; -361 map</*op*/string, string> Name_0f; -362 map</*op*/string, string> Name_f3; -363 map</*op*/string, string> Name_f3_0f; -364 :(before "End One-time Setup") -365 init_op_names(); -366 :(code) -367 void init_op_names() { -368 put(Name, "f4", "halt (hlt)"); -369 // End Initialize Op Names -370 } -371 -372 :(before "End Help Special-cases(key)") -373 if (key == "opcodes") { -374 cerr << "Opcodes currently supported by SubX:\n"; -375 for (map<string, string>::iterator p = Name.begin(); p != Name.end(); ++p) -376 cerr << " " << p->first << ": " << p->second << '\n'; -377 for (map<string, string>::iterator p = Name_0f.begin(); p != Name_0f.end(); ++p) -378 cerr << " 0f " << p->first << ": " << p->second << '\n'; -379 for (map<string, string>::iterator p = Name_f3.begin(); p != Name_f3.end(); ++p) -380 cerr << " f3 " << p->first << ": " << p->second << '\n'; -381 for (map<string, string>::iterator p = Name_f3_0f.begin(); p != Name_f3_0f.end(); ++p) -382 cerr << " f3 0f " << p->first << ": " << p->second << '\n'; -383 cerr << "Run `subx help instructions` for details on words like 'r32' and 'disp8'.\n" -384 "For complete details on these instructions, consult the IA-32 manual (volume 2).\n" -385 "There's various versions of it online, such as https://c9x.me/x86.\n" -386 "The mnemonics in brackets will help you locate each instruction.\n"; -387 return 0; -388 } -389 :(before "End Help Contents") -390 cerr << " opcodes\n"; -391 -392 //: Helpers for managing trace depths -393 //: -394 //: We're going to use trace depths primarily to segment code running at -395 //: different frames of the call stack. This will make it easy for the trace -396 //: browser to collapse over entire calls. -397 //: -398 //: Errors will be at depth 0. -399 //: Warnings will be at depth 1. -400 //: SubX instructions will occupy depth 2 and up to Max_depth, organized by -401 //: stack frames. Each instruction's internal details will be one level deeper -402 //: than its 'main' depth. So 'call' instruction details will be at the same -403 //: depth as the instructions of the function it calls. -404 :(before "End Globals") -405 extern const int Initial_callstack_depth = 2; -406 int Callstack_depth = Initial_callstack_depth; -407 :(before "End Reset") -408 Callstack_depth = Initial_callstack_depth; -409 -410 :(before "End Includes") -411 #include <iomanip> -412 #define HEXBYTE std::hex << std::setw(2) << std::setfill('0') -413 #define HEXWORD std::hex << std::setw(8) << std::setfill('0') -414 // ugly that iostream doesn't print uint8_t as an integer -415 #define NUM(X) static_cast<int>(X) -416 #include <stdint.h> +307 cerr << "unrecognized third opcode after f2 0f: " << HEXBYTE << NUM(op3) << '\n'; +308 exit(1); +309 } +310 break; +311 default: +312 cerr << "unrecognized second opcode after f2: " << HEXBYTE << NUM(op2) << '\n'; +313 exit(1); +314 } +315 break; +316 case 0xf3: +317 switch(op2 = next()) { +318 // End Two-Byte Opcodes Starting With f3 +319 case 0x0f: +320 switch(op3 = next()) { +321 // End Three-Byte Opcodes Starting With f3 0f +322 default: +323 cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n'; +324 exit(1); +325 } +326 break; +327 default: +328 cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n'; +329 exit(1); +330 } +331 break; +332 default: +333 cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n'; +334 exit(1); +335 } +336 } +337 +338 inline uint8_t next() { +339 return read_mem_u8(EIP++); +340 } +341 +342 void dump_registers() { +343 ostringstream out; +344 out << "registers before: "; +345 for (int i = 0; i < NUM_INT_REGISTERS; ++i) { +346 if (i > 0) out << "; "; +347 out << " " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u; +348 } +349 out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF; +350 trace(Callstack_depth+1, "run") << out.str() << end(); +351 } +352 +353 //: start tracking supported opcodes +354 :(before "End Globals") +355 map</*op*/string, string> Name; +356 map</*op*/string, string> Name_0f; +357 map</*op*/string, string> Name_f3; +358 map</*op*/string, string> Name_f3_0f; +359 :(before "End One-time Setup") +360 init_op_names(); +361 :(code) +362 void init_op_names() { +363 put(Name, "f4", "halt (hlt)"); +364 // End Initialize Op Names +365 } +366 +367 :(before "End Help Special-cases(key)") +368 if (key == "opcodes") { +369 cerr << "Opcodes currently supported by SubX:\n"; +370 for (map<string, string>::iterator p = Name.begin(); p != Name.end(); ++p) +371 cerr << " " << p->first << ": " << p->second << '\n'; +372 for (map<string, string>::iterator p = Name_0f.begin(); p != Name_0f.end(); ++p) +373 cerr << " 0f " << p->first << ": " << p->second << '\n'; +374 for (map<string, string>::iterator p = Name_f3.begin(); p != Name_f3.end(); ++p) +375 cerr << " f3 " << p->first << ": " << p->second << '\n'; +376 for (map<string, string>::iterator p = Name_f3_0f.begin(); p != Name_f3_0f.end(); ++p) +377 cerr << " f3 0f " << p->first << ": " << p->second << '\n'; +378 cerr << "Run `subx help instructions` for details on words like 'r32' and 'disp8'.\n" +379 "For complete details on these instructions, consult the IA-32 manual (volume 2).\n" +380 "There's various versions of it online, such as https://c9x.me/x86.\n" +381 "The mnemonics in brackets will help you locate each instruction.\n"; +382 return 0; +383 } +384 :(before "End Help Contents") +385 cerr << " opcodes\n"; +386 +387 //: Helpers for managing trace depths +388 //: +389 //: We're going to use trace depths primarily to segment code running at +390 //: different frames of the call stack. This will make it easy for the trace +391 //: browser to collapse over entire calls. +392 //: +393 //: Errors will be at depth 0. +394 //: Warnings will be at depth 1. +395 //: SubX instructions will occupy depth 2 and up to Max_depth, organized by +396 //: stack frames. Each instruction's internal details will be one level deeper +397 //: than its 'main' depth. So 'call' instruction details will be at the same +398 //: depth as the instructions of the function it calls. +399 :(before "End Globals") +400 extern const int Initial_callstack_depth = 2; +401 int Callstack_depth = Initial_callstack_depth; +402 :(before "End Reset") +403 Callstack_depth = Initial_callstack_depth; +404 +405 :(before "End Includes") +406 #include <iomanip> +407 #define HEXBYTE std::hex << std::setw(2) << std::setfill('0') +408 #define HEXWORD std::hex << std::setw(8) << std::setfill('0') +409 // ugly that iostream doesn't print uint8_t as an integer +410 #define NUM(X) static_cast<int>(X) +411 #include <stdint.h> diff --git a/html/subx/011run.cc.html b/html/subx/011run.cc.html index 2bbfce44..63ac2fab 100644 --- a/html/subx/011run.cc.html +++ b/html/subx/011run.cc.html @@ -3,8 +3,8 @@ Mu - subx/011run.cc - - + + @@ -14,14 +14,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.cSpecial { color: #008000; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.cSpecial { color: #008000; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } .SalientComment { color: #0000af; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -152,7 +152,7 @@ if ('onhashchange' in window) { 92 load(p); 93 if (trace_contains_errors()) return; 94 while (EIP < End_of_program) - 95 run_one_instruction(); + 95 run_one_instruction(); 96 } 97 98 //:: core data structures @@ -227,7 +227,7 @@ if ('onhashchange' in window) { 167 curr_segment->name = segment_name; 168 curr_segment->start = seg_start; 169 if (trace_contains_errors()) continue; -170 trace(3, "parse") << "starts at address 0x" << HEXWORD << curr_segment->start << end(); +170 trace(3, "parse") << "starts at address 0x" << HEXWORD << curr_segment->start << end(); 171 } 172 break; // skip rest of line 173 } @@ -329,7 +329,7 @@ if ('onhashchange' in window) { 269 uint32_t addr = seg.start; 270 if (!already_allocated(addr)) 271 Mem.push_back(vma(seg.start)); -272 trace(99, "load") << "loading segment " << i << " from " << HEXWORD << addr << end(); +272 trace(99, "load") << "loading segment " << i << " from " << HEXWORD << addr << end(); 273 for (int j = 0; j < SIZE(seg.lines); ++j) { 274 const line& l = seg.lines.at(j); 275 for (int k = 0; k < SIZE(l.words); ++k) { @@ -337,9 +337,9 @@ if ('onhashchange' in window) { 277 uint8_t val = hex_byte(w.data); 278 if (trace_contains_errors()) return; 279 assert(overlap.find(addr) == overlap.end()); -280 write_mem_u8(addr, val); +280 write_mem_u8(addr, val); 281 overlap.insert(addr); -282 trace(99, "load") << "0x" << HEXWORD << addr << " -> " << HEXBYTE << NUM(read_mem_u8(addr)) << end(); +282 trace(99, "load") << "0x" << HEXWORD << addr << " -> " << HEXBYTE << NUM(read_mem_u8(addr)) << end(); 283 ++addr; 284 } 285 } @@ -464,14 +464,14 @@ if ('onhashchange' in window) { 404 //:: run 405 406 :(before "End Initialize Op Names") -407 put_new(Name, "b8", "copy imm32 to EAX (mov)"); +407 put_new(Name, "b8", "copy imm32 to EAX (mov)"); 408 409 //: our first opcode 410 411 :(before "End Single-Byte Opcodes") 412 case 0xb8: { // copy imm32 to EAX 413 const int32_t src = next32(); -414 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to EAX" << end(); +414 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to EAX" << end(); 415 Reg[EAX].i = src; 416 break; 417 } diff --git a/html/subx/012elf.cc.html b/html/subx/012elf.cc.html index 145576f3..ce4edb34 100644 --- a/html/subx/012elf.cc.html +++ b/html/subx/012elf.cc.html @@ -3,8 +3,8 @@ Mu - subx/012elf.cc - - + + @@ -15,14 +15,14 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .CommentedCode { color: #8a8a8a; } -.PreProc { color: #c000c0; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } +.PreProc { color: #c000c0; } .cSpecial { color: #008000; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -73,7 +73,7 @@ if ('onhashchange' in window) { 12 cerr << std::hex; 13 load_elf(argv[2], argc, argv); 14 while (EIP < End_of_program) // weak final-gasp termination check - 15 run_one_instruction(); + 15 run_one_instruction(); 16 raise << "executed past end of the world: " << EIP << " vs " << End_of_program << '\n' << end(); 17 return 1; 18 } @@ -104,7 +104,7 @@ if ('onhashchange' in window) { 43 // unused: remaining 10 bytes of e_ident 44 uint32_t e_machine_type = u32_in(&elf_contents[16]); 45 if (e_machine_type != 0x00030002) - 46 raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die(); + 46 raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die(); 47 // unused: e_version. We only support version 1, and later versions will be backwards compatible. 48 uint32_t e_entry = u32_in(&elf_contents[24]); 49 uint32_t e_phoff = u32_in(&elf_contents[28]); @@ -141,7 +141,7 @@ if ('onhashchange' in window) { 80 push(argv_data); 81 for (size_t j = 0; j <= strlen(argv[i]); ++j) { 82 assert(overlap.find(argv_data) == overlap.end()); // don't bother comparing ARGV and STACK - 83 write_mem_u8(argv_data, argv[i][j]); + 83 write_mem_u8(argv_data, argv[i][j]); 84 argv_data += sizeof(char); 85 assert(argv_data < ARGV_DATA_SEGMENT + SEGMENT_ALIGNMENT); 86 } @@ -158,9 +158,9 @@ if ('onhashchange' in window) { 97 << "to be larger.\n" << end(); 98 exit(1); 99 } -100 trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); -101 trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end(); -102 write_mem_u32(Reg[ESP].u, val); +100 trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); +101 trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end(); +102 write_mem_u32(Reg[ESP].u, val); 103 } 104 105 void load_segment_from_program_header(uint8_t* elf_contents, int segment_index, size_t size, uint32_t offset, uint32_t e_ehsize, set<uint32_t>& overlap) { @@ -190,7 +190,7 @@ if ('onhashchange' in window) { 129 Mem.push_back(vma(p_vaddr)); 130 for (size_t i = 0; i < p_filesz; ++i) { 131 assert(overlap.find(p_vaddr+i) == overlap.end()); -132 write_mem_u8(p_vaddr+i, elf_contents[p_offset+i]); +132 write_mem_u8(p_vaddr+i, elf_contents[p_offset+i]); 133 overlap.insert(p_vaddr+i); 134 } 135 if (segment_index == 0 && End_of_program < p_vaddr+p_memsz) @@ -220,10 +220,10 @@ if ('onhashchange' in window) { 159 ostringstream out; 160 trace(Callstack_depth+1, "run") << "stack:" << end(); 161 for (uint32_t a = AFTER_STACK-4; a > Reg[ESP].u; a -= 4) -162 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); -163 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << " <=== ESP" << end(); +162 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); +163 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << " <=== ESP" << end(); 164 for (uint32_t a = Reg[ESP].u-4; a > Reg[ESP].u-40; a -= 4) -165 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); +165 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); 166 } 167 168 inline uint32_t u32_in(uint8_t* p) { diff --git a/html/subx/013direct_addressing.cc.html b/html/subx/013direct_addressing.cc.html index 8899559f..1c4a0346 100644 --- a/html/subx/013direct_addressing.cc.html +++ b/html/subx/013direct_addressing.cc.html @@ -3,8 +3,8 @@ Mu - subx/013direct_addressing.cc - - + + @@ -16,13 +16,13 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .CommentedCode { color: #8a8a8a; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .SalientComment { color: #0000af; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } .cSpecial { color: #008000; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -62,7 +62,7 @@ if ('onhashchange' in window) { 1 //: operating directly on a register 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "01", "add r32 to rm32 (add)"); + 4 put_new(Name, "01", "add r32 to rm32 (add)"); 5 6 :(code) 7 void test_add_r32_to_r32() { @@ -83,7 +83,7 @@ if ('onhashchange' in window) { 22 23 :(before "End Single-Byte Opcodes") 24 case 0x01: { // add r32 to r/m32 - 25 uint8_t modrm = next(); + 25 uint8_t modrm = next(); 26 uint8_t arg2 = (modrm>>3)&0x7; 27 trace(Callstack_depth+1, "run") << "add " << rname(arg2) << " to r/m32" << end(); 28 int32_t* signed_arg1 = effective_address(modrm); @@ -99,7 +99,7 @@ if ('onhashchange' in window) { 38 CF = (unsigned_result != unsigned_full_result); 39 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 40 *signed_arg1 = signed_result; - 41 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 41 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 42 break; 43 } 44 @@ -170,7 +170,7 @@ if ('onhashchange' in window) { 109 } 110 uint32_t addr = effective_address_number(modrm); 111 trace(Callstack_depth+1, "run") << "effective address contains " << read_mem_i32(addr) << end(); - 112 return mem_addr_i32(addr); + 112 return mem_addr_i32(addr); 113 } 114 115 // beware: will eventually have side-effects @@ -186,7 +186,7 @@ if ('onhashchange' in window) { 125 return 0; 126 // End Mod Special-cases(addr) 127 default: - 128 cerr << "unrecognized mod bits: " << NUM(mod) << '\n'; + 128 cerr << "unrecognized mod bits: " << NUM(mod) << '\n'; 129 exit(1); 130 } 131 //: other mods are indirect, and they'll set addr appropriately @@ -211,7 +211,7 @@ if ('onhashchange' in window) { 150 //:: subtract 151 152 :(before "End Initialize Op Names") - 153 put_new(Name, "29", "subtract r32 from rm32 (sub)"); + 153 put_new(Name, "29", "subtract r32 from rm32 (sub)"); 154 155 :(code) 156 void test_subtract_r32_from_r32() { @@ -232,7 +232,7 @@ if ('onhashchange' in window) { 171 172 :(before "End Single-Byte Opcodes") 173 case 0x29: { // subtract r32 from r/m32 - 174 const uint8_t modrm = next(); + 174 const uint8_t modrm = next(); 175 const uint8_t arg2 = (modrm>>3)&0x7; 176 trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end(); 177 int32_t* signed_arg1 = effective_address(modrm); @@ -248,7 +248,7 @@ if ('onhashchange' in window) { 187 CF = (unsigned_result != unsigned_full_result); 188 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 189 *signed_arg1 = signed_result; - 190 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 190 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 191 break; 192 } 193 @@ -307,7 +307,7 @@ if ('onhashchange' in window) { 246 //:: multiply 247 248 :(before "End Initialize Op Names") - 249 put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)"); + 249 put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)"); 250 251 :(code) 252 void test_multiply_EAX_by_r32() { @@ -329,7 +329,7 @@ if ('onhashchange' in window) { 268 269 :(before "End Single-Byte Opcodes") 270 case 0xf7: { - 271 const uint8_t modrm = next(); + 271 const uint8_t modrm = next(); 272 trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); 273 int32_t* arg1 = effective_address(modrm); 274 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits @@ -342,12 +342,12 @@ if ('onhashchange' in window) { 281 OF = (Reg[EDX].u != 0); 282 CF = OF; 283 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 284 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end(); + 284 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end(); 285 break; 286 } 287 // End Op f7 Subops 288 default: - 289 cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n'; + 289 cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n'; 290 exit(1); 291 } 292 break; @@ -356,7 +356,7 @@ if ('onhashchange' in window) { 295 //: 296 297 :(before "End Initialize Op Names") - 298 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); + 298 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); 299 300 :(code) 301 void test_multiply_r32_into_r32() { @@ -377,7 +377,7 @@ if ('onhashchange' in window) { 316 317 :(before "End Two-Byte Opcodes Starting With 0f") 318 case 0xaf: { // multiply r32 by r/m32 - 319 const uint8_t modrm = next(); + 319 const uint8_t modrm = next(); 320 const uint8_t arg1 = (modrm>>3)&0x7; 321 trace(Callstack_depth+1, "run") << "multiply " << rname(arg1) << " by r/m32" << end(); 322 const int32_t* arg2 = effective_address(modrm); @@ -389,7 +389,7 @@ if ('onhashchange' in window) { 328 CF = OF; 329 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 330 Reg[arg1].i = result; - 331 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + 331 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); 332 break; 333 } 334 @@ -430,7 +430,7 @@ if ('onhashchange' in window) { 369 CF = (*arg1 != 0); 370 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 371 *arg1 = result; - 372 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 372 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 373 break; 374 } 375 @@ -482,8 +482,8 @@ if ('onhashchange' in window) { 421 Reg[EAX].i = dividend/divisor; // quotient 422 Reg[EDX].i = dividend%divisor; // remainder 423 // flag state undefined - 424 trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end(); - 425 trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end(); + 424 trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end(); + 425 trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end(); 426 break; 427 } 428 @@ -548,7 +548,7 @@ if ('onhashchange' in window) { 487 //:: shift left 488 489 :(before "End Initialize Op Names") - 490 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); + 490 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); 491 492 :(code) 493 void test_shift_left_r32_with_cl() { @@ -570,7 +570,7 @@ if ('onhashchange' in window) { 509 510 :(before "End Single-Byte Opcodes") 511 case 0xd3: { - 512 const uint8_t modrm = next(); + 512 const uint8_t modrm = next(); 513 trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); 514 int32_t* arg1 = effective_address(modrm); 515 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits @@ -590,12 +590,12 @@ if ('onhashchange' in window) { 529 CF = (*arg1 << (count-1)) & 0x80000000; 530 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 531 *arg1 = result; - 532 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 532 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 533 break; 534 } 535 // End Op d3 Subops 536 default: - 537 cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n'; + 537 cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n'; 538 exit(1); 539 } 540 break; @@ -631,7 +631,7 @@ if ('onhashchange' in window) { 570 // OF is only defined if count is 1 571 if (count == 1) OF = false; 572 // CF undefined - 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 574 break; 575 } 576 @@ -709,7 +709,7 @@ if ('onhashchange' in window) { 648 // result is always positive by definition 649 SF = false; 650 // CF undefined - 651 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 651 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 652 break; 653 } 654 @@ -752,7 +752,7 @@ if ('onhashchange' in window) { 691 //:: and 692 693 :(before "End Initialize Op Names") - 694 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); + 694 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); 695 696 :(code) 697 void test_and_r32_with_r32() { @@ -773,14 +773,14 @@ if ('onhashchange' in window) { 712 713 :(before "End Single-Byte Opcodes") 714 case 0x21: { // and r32 with r/m32 - 715 const uint8_t modrm = next(); + 715 const uint8_t modrm = next(); 716 const uint8_t arg2 = (modrm>>3)&0x7; 717 trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end(); 718 // bitwise ops technically operate on unsigned numbers, but it makes no 719 // difference 720 int32_t* signed_arg1 = effective_address(modrm); 721 *signed_arg1 &= Reg[arg2].i; - 722 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 722 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 723 SF = (*signed_arg1 >> 31); 724 ZF = (*signed_arg1 == 0); 725 CF = false; @@ -792,7 +792,7 @@ if ('onhashchange' in window) { 731 //:: or 732 733 :(before "End Initialize Op Names") - 734 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); + 734 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); 735 736 :(code) 737 void test_or_r32_with_r32() { @@ -813,14 +813,14 @@ if ('onhashchange' in window) { 752 753 :(before "End Single-Byte Opcodes") 754 case 0x09: { // or r32 with r/m32 - 755 const uint8_t modrm = next(); + 755 const uint8_t modrm = next(); 756 const uint8_t arg2 = (modrm>>3)&0x7; 757 trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end(); 758 // bitwise ops technically operate on unsigned numbers, but it makes no 759 // difference 760 int32_t* signed_arg1 = effective_address(modrm); 761 *signed_arg1 |= Reg[arg2].i; - 762 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 762 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 763 SF = (*signed_arg1 >> 31); 764 ZF = (*signed_arg1 == 0); 765 CF = false; @@ -832,7 +832,7 @@ if ('onhashchange' in window) { 771 //:: xor 772 773 :(before "End Initialize Op Names") - 774 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); + 774 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); 775 776 :(code) 777 void test_xor_r32_with_r32() { @@ -853,14 +853,14 @@ if ('onhashchange' in window) { 792 793 :(before "End Single-Byte Opcodes") 794 case 0x31: { // xor r32 with r/m32 - 795 const uint8_t modrm = next(); + 795 const uint8_t modrm = next(); 796 const uint8_t arg2 = (modrm>>3)&0x7; 797 trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end(); 798 // bitwise ops technically operate on unsigned numbers, but it makes no 799 // difference 800 int32_t* signed_arg1 = effective_address(modrm); 801 *signed_arg1 ^= Reg[arg2].i; - 802 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 802 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 803 SF = (*signed_arg1 >> 31); 804 ZF = (*signed_arg1 == 0); 805 CF = false; @@ -892,7 +892,7 @@ if ('onhashchange' in window) { 831 case 2: { // not r/m32 832 trace(Callstack_depth+1, "run") << "subop: not" << end(); 833 *arg1 = ~(*arg1); - 834 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 834 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 835 // no flags affected 836 break; 837 } @@ -900,7 +900,7 @@ if ('onhashchange' in window) { 839 //:: compare (cmp) 840 841 :(before "End Initialize Op Names") - 842 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); + 842 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); 843 844 :(code) 845 void test_compare_r32_with_r32_greater() { @@ -921,7 +921,7 @@ if ('onhashchange' in window) { 860 861 :(before "End Single-Byte Opcodes") 862 case 0x39: { // set SF if r/m32 < r32 - 863 const uint8_t modrm = next(); + 863 const uint8_t modrm = next(); 864 const uint8_t reg2 = (modrm>>3)&0x7; 865 trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg2) << end(); 866 const int32_t* signed_arg1 = effective_address(modrm); @@ -1023,7 +1023,7 @@ if ('onhashchange' in window) { 962 //:: copy (mov) 963 964 :(before "End Initialize Op Names") - 965 put_new(Name, "89", "copy r32 to rm32 (mov)"); + 965 put_new(Name, "89", "copy r32 to rm32 (mov)"); 966 967 :(code) 968 void test_copy_r32_to_r32() { @@ -1043,19 +1043,19 @@ if ('onhashchange' in window) { 982 983 :(before "End Single-Byte Opcodes") 984 case 0x89: { // copy r32 to r/m32 - 985 const uint8_t modrm = next(); + 985 const uint8_t modrm = next(); 986 const uint8_t rsrc = (modrm>>3)&0x7; 987 trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end(); 988 int32_t* dest = effective_address(modrm); 989 *dest = Reg[rsrc].i; - 990 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end(); + 990 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end(); 991 break; 992 } 993 994 //:: xchg 995 996 :(before "End Initialize Op Names") - 997 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); + 997 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); 998 999 :(code) 1000 void test_xchg_r32_with_r32() { @@ -1077,29 +1077,29 @@ if ('onhashchange' in window) { 1016 1017 :(before "End Single-Byte Opcodes") 1018 case 0x87: { // exchange r32 with r/m32 -1019 const uint8_t modrm = next(); +1019 const uint8_t modrm = next(); 1020 const uint8_t reg2 = (modrm>>3)&0x7; 1021 trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end(); 1022 int32_t* arg1 = effective_address(modrm); 1023 const int32_t tmp = *arg1; 1024 *arg1 = Reg[reg2].i; 1025 Reg[reg2].i = tmp; -1026 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end(); -1027 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end(); +1026 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end(); +1027 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end(); 1028 break; 1029 } 1030 1031 //:: increment 1032 1033 :(before "End Initialize Op Names") -1034 put_new(Name, "40", "increment EAX (inc)"); -1035 put_new(Name, "41", "increment ECX (inc)"); -1036 put_new(Name, "42", "increment EDX (inc)"); -1037 put_new(Name, "43", "increment EBX (inc)"); -1038 put_new(Name, "44", "increment ESP (inc)"); -1039 put_new(Name, "45", "increment EBP (inc)"); -1040 put_new(Name, "46", "increment ESI (inc)"); -1041 put_new(Name, "47", "increment EDI (inc)"); +1034 put_new(Name, "40", "increment EAX (inc)"); +1035 put_new(Name, "41", "increment ECX (inc)"); +1036 put_new(Name, "42", "increment EDX (inc)"); +1037 put_new(Name, "43", "increment EBX (inc)"); +1038 put_new(Name, "44", "increment ESP (inc)"); +1039 put_new(Name, "45", "increment EBP (inc)"); +1040 put_new(Name, "46", "increment ESI (inc)"); +1041 put_new(Name, "47", "increment EDI (inc)"); 1042 1043 :(code) 1044 void test_increment_r32() { @@ -1127,12 +1127,12 @@ if ('onhashchange' in window) { 1066 const uint8_t reg = op & 0x7; 1067 trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end(); 1068 ++Reg[reg].u; -1069 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); +1069 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); 1070 break; 1071 } 1072 1073 :(before "End Initialize Op Names") -1074 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); +1074 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); 1075 1076 :(code) 1077 void test_increment_rm32() { @@ -1152,193 +1152,192 @@ if ('onhashchange' in window) { 1091 1092 :(before "End Single-Byte Opcodes") 1093 case 0xff: { -1094 const uint8_t modrm = next(); +1094 const uint8_t modrm = next(); 1095 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 1096 switch (subop) { 1097 case 0: { // increment r/m32 1098 trace(Callstack_depth+1, "run") << "increment r/m32" << end(); 1099 int32_t* arg = effective_address(modrm); 1100 ++*arg; -1101 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); +1101 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); 1102 break; 1103 } 1104 default: -1105 cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n'; -1106 DUMP(""); -1107 exit(1); -1108 // End Op ff Subops -1109 } -1110 break; -1111 } -1112 -1113 //:: decrement -1114 -1115 :(before "End Initialize Op Names") -1116 put_new(Name, "48", "decrement EAX (dec)"); -1117 put_new(Name, "49", "decrement ECX (dec)"); -1118 put_new(Name, "4a", "decrement EDX (dec)"); -1119 put_new(Name, "4b", "decrement EBX (dec)"); -1120 put_new(Name, "4c", "decrement ESP (dec)"); -1121 put_new(Name, "4d", "decrement EBP (dec)"); -1122 put_new(Name, "4e", "decrement ESI (dec)"); -1123 put_new(Name, "4f", "decrement EDI (dec)"); -1124 -1125 :(code) -1126 void test_decrement_r32() { -1127 Reg[ECX].u = 0x1f; -1128 run( -1129 "== code 0x1\n" // code segment -1130 // op ModR/M SIB displacement immediate -1131 " 49 \n" // decrement ECX -1132 ); -1133 CHECK_TRACE_CONTENTS( -1134 "run: decrement ECX\n" -1135 "run: storing value 0x0000001e\n" -1136 ); -1137 } -1138 -1139 :(before "End Single-Byte Opcodes") -1140 case 0x48: -1141 case 0x49: -1142 case 0x4a: -1143 case 0x4b: -1144 case 0x4c: -1145 case 0x4d: -1146 case 0x4e: -1147 case 0x4f: { // decrement r32 -1148 const uint8_t reg = op & 0x7; -1149 trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end(); -1150 --Reg[reg].u; -1151 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); -1152 break; -1153 } -1154 -1155 :(code) -1156 void test_decrement_rm32() { -1157 Reg[EAX].u = 0x20; -1158 run( -1159 "== code 0x1\n" // code segment -1160 // op ModR/M SIB displacement immediate -1161 " ff c8 \n" // decrement EAX -1162 // ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX) -1163 ); -1164 CHECK_TRACE_CONTENTS( -1165 "run: decrement r/m32\n" -1166 "run: r/m32 is EAX\n" -1167 "run: storing value 0x0000001f\n" -1168 ); -1169 } -1170 -1171 :(before "End Op ff Subops") -1172 case 1: { // decrement r/m32 -1173 trace(Callstack_depth+1, "run") << "decrement r/m32" << end(); -1174 int32_t* arg = effective_address(modrm); -1175 --*arg; -1176 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); -1177 break; -1178 } -1179 -1180 //:: push -1181 -1182 :(before "End Initialize Op Names") -1183 put_new(Name, "50", "push EAX to stack (push)"); -1184 put_new(Name, "51", "push ECX to stack (push)"); -1185 put_new(Name, "52", "push EDX to stack (push)"); -1186 put_new(Name, "53", "push EBX to stack (push)"); -1187 put_new(Name, "54", "push ESP to stack (push)"); -1188 put_new(Name, "55", "push EBP to stack (push)"); -1189 put_new(Name, "56", "push ESI to stack (push)"); -1190 put_new(Name, "57", "push EDI to stack (push)"); -1191 -1192 :(code) -1193 void test_push_r32() { -1194 Mem.push_back(vma(0xbd000000)); // manually allocate memory -1195 Reg[ESP].u = 0xbd000008; -1196 Reg[EBX].i = 0x0000000a; -1197 run( -1198 "== code 0x1\n" // code segment -1199 // op ModR/M SIB displacement immediate -1200 " 53 \n" // push EBX to stack -1201 ); -1202 CHECK_TRACE_CONTENTS( -1203 "run: push EBX\n" -1204 "run: decrementing ESP to 0xbd000004\n" -1205 "run: pushing value 0x0000000a\n" -1206 ); -1207 } -1208 -1209 :(before "End Single-Byte Opcodes") -1210 case 0x50: -1211 case 0x51: -1212 case 0x52: -1213 case 0x53: -1214 case 0x54: -1215 case 0x55: -1216 case 0x56: -1217 case 0x57: { // push r32 to stack -1218 uint8_t reg = op & 0x7; -1219 trace(Callstack_depth+1, "run") << "push " << rname(reg) << end(); -1220 //? cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n'; -1221 push(Reg[reg].u); -1222 break; -1223 } -1224 -1225 //:: pop -1226 -1227 :(before "End Initialize Op Names") -1228 put_new(Name, "58", "pop top of stack to EAX (pop)"); -1229 put_new(Name, "59", "pop top of stack to ECX (pop)"); -1230 put_new(Name, "5a", "pop top of stack to EDX (pop)"); -1231 put_new(Name, "5b", "pop top of stack to EBX (pop)"); -1232 put_new(Name, "5c", "pop top of stack to ESP (pop)"); -1233 put_new(Name, "5d", "pop top of stack to EBP (pop)"); -1234 put_new(Name, "5e", "pop top of stack to ESI (pop)"); -1235 put_new(Name, "5f", "pop top of stack to EDI (pop)"); -1236 -1237 :(code) -1238 void test_pop_r32() { -1239 Mem.push_back(vma(0xbd000000)); // manually allocate memory -1240 Reg[ESP].u = 0xbd000008; -1241 write_mem_i32(0xbd000008, 0x0000000a); // ..before this write -1242 run( -1243 "== code 0x1\n" // code segment -1244 // op ModR/M SIB displacement immediate -1245 " 5b \n" // pop stack to EBX -1246 "== data 0x2000\n" // data segment -1247 "0a 00 00 00\n" // 0x0000000a -1248 ); -1249 CHECK_TRACE_CONTENTS( -1250 "run: pop into EBX\n" -1251 "run: popping value 0x0000000a\n" -1252 "run: incrementing ESP to 0xbd00000c\n" -1253 ); -1254 } -1255 -1256 :(before "End Single-Byte Opcodes") -1257 case 0x58: -1258 case 0x59: -1259 case 0x5a: -1260 case 0x5b: -1261 case 0x5c: -1262 case 0x5d: -1263 case 0x5e: -1264 case 0x5f: { // pop stack into r32 -1265 const uint8_t reg = op & 0x7; -1266 trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end(); -1267 //? cerr << "pop from " << Reg[ESP].u << '\n'; -1268 Reg[reg].u = pop(); -1269 //? cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n'; -1270 break; -1271 } -1272 :(code) -1273 uint32_t pop() { -1274 const uint32_t result = read_mem_u32(Reg[ESP].u); -1275 trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end(); -1276 Reg[ESP].u += 4; -1277 trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); -1278 assert(Reg[ESP].u < AFTER_STACK); -1279 return result; -1280 } +1105 cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n'; +1106 exit(1); +1107 // End Op ff Subops +1108 } +1109 break; +1110 } +1111 +1112 //:: decrement +1113 +1114 :(before "End Initialize Op Names") +1115 put_new(Name, "48", "decrement EAX (dec)"); +1116 put_new(Name, "49", "decrement ECX (dec)"); +1117 put_new(Name, "4a", "decrement EDX (dec)"); +1118 put_new(Name, "4b", "decrement EBX (dec)"); +1119 put_new(Name, "4c", "decrement ESP (dec)"); +1120 put_new(Name, "4d", "decrement EBP (dec)"); +1121 put_new(Name, "4e", "decrement ESI (dec)"); +1122 put_new(Name, "4f", "decrement EDI (dec)"); +1123 +1124 :(code) +1125 void test_decrement_r32() { +1126 Reg[ECX].u = 0x1f; +1127 run( +1128 "== code 0x1\n" // code segment +1129 // op ModR/M SIB displacement immediate +1130 " 49 \n" // decrement ECX +1131 ); +1132 CHECK_TRACE_CONTENTS( +1133 "run: decrement ECX\n" +1134 "run: storing value 0x0000001e\n" +1135 ); +1136 } +1137 +1138 :(before "End Single-Byte Opcodes") +1139 case 0x48: +1140 case 0x49: +1141 case 0x4a: +1142 case 0x4b: +1143 case 0x4c: +1144 case 0x4d: +1145 case 0x4e: +1146 case 0x4f: { // decrement r32 +1147 const uint8_t reg = op & 0x7; +1148 trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end(); +1149 --Reg[reg].u; +1150 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); +1151 break; +1152 } +1153 +1154 :(code) +1155 void test_decrement_rm32() { +1156 Reg[EAX].u = 0x20; +1157 run( +1158 "== code 0x1\n" // code segment +1159 // op ModR/M SIB displacement immediate +1160 " ff c8 \n" // decrement EAX +1161 // ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX) +1162 ); +1163 CHECK_TRACE_CONTENTS( +1164 "run: decrement r/m32\n" +1165 "run: r/m32 is EAX\n" +1166 "run: storing value 0x0000001f\n" +1167 ); +1168 } +1169 +1170 :(before "End Op ff Subops") +1171 case 1: { // decrement r/m32 +1172 trace(Callstack_depth+1, "run") << "decrement r/m32" << end(); +1173 int32_t* arg = effective_address(modrm); +1174 --*arg; +1175 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); +1176 break; +1177 } +1178 +1179 //:: push +1180 +1181 :(before "End Initialize Op Names") +1182 put_new(Name, "50", "push EAX to stack (push)"); +1183 put_new(Name, "51", "push ECX to stack (push)"); +1184 put_new(Name, "52", "push EDX to stack (push)"); +1185 put_new(Name, "53", "push EBX to stack (push)"); +1186 put_new(Name, "54", "push ESP to stack (push)"); +1187 put_new(Name, "55", "push EBP to stack (push)"); +1188 put_new(Name, "56", "push ESI to stack (push)"); +1189 put_new(Name, "57", "push EDI to stack (push)"); +1190 +1191 :(code) +1192 void test_push_r32() { +1193 Mem.push_back(vma(0xbd000000)); // manually allocate memory +1194 Reg[ESP].u = 0xbd000008; +1195 Reg[EBX].i = 0x0000000a; +1196 run( +1197 "== code 0x1\n" // code segment +1198 // op ModR/M SIB displacement immediate +1199 " 53 \n" // push EBX to stack +1200 ); +1201 CHECK_TRACE_CONTENTS( +1202 "run: push EBX\n" +1203 "run: decrementing ESP to 0xbd000004\n" +1204 "run: pushing value 0x0000000a\n" +1205 ); +1206 } +1207 +1208 :(before "End Single-Byte Opcodes") +1209 case 0x50: +1210 case 0x51: +1211 case 0x52: +1212 case 0x53: +1213 case 0x54: +1214 case 0x55: +1215 case 0x56: +1216 case 0x57: { // push r32 to stack +1217 uint8_t reg = op & 0x7; +1218 trace(Callstack_depth+1, "run") << "push " << rname(reg) << end(); +1219 //? cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n'; +1220 push(Reg[reg].u); +1221 break; +1222 } +1223 +1224 //:: pop +1225 +1226 :(before "End Initialize Op Names") +1227 put_new(Name, "58", "pop top of stack to EAX (pop)"); +1228 put_new(Name, "59", "pop top of stack to ECX (pop)"); +1229 put_new(Name, "5a", "pop top of stack to EDX (pop)"); +1230 put_new(Name, "5b", "pop top of stack to EBX (pop)"); +1231 put_new(Name, "5c", "pop top of stack to ESP (pop)"); +1232 put_new(Name, "5d", "pop top of stack to EBP (pop)"); +1233 put_new(Name, "5e", "pop top of stack to ESI (pop)"); +1234 put_new(Name, "5f", "pop top of stack to EDI (pop)"); +1235 +1236 :(code) +1237 void test_pop_r32() { +1238 Mem.push_back(vma(0xbd000000)); // manually allocate memory +1239 Reg[ESP].u = 0xbd000008; +1240 write_mem_i32(0xbd000008, 0x0000000a); // ..before this write +1241 run( +1242 "== code 0x1\n" // code segment +1243 // op ModR/M SIB displacement immediate +1244 " 5b \n" // pop stack to EBX +1245 "== data 0x2000\n" // data segment +1246 "0a 00 00 00\n" // 0x0000000a +1247 ); +1248 CHECK_TRACE_CONTENTS( +1249 "run: pop into EBX\n" +1250 "run: popping value 0x0000000a\n" +1251 "run: incrementing ESP to 0xbd00000c\n" +1252 ); +1253 } +1254 +1255 :(before "End Single-Byte Opcodes") +1256 case 0x58: +1257 case 0x59: +1258 case 0x5a: +1259 case 0x5b: +1260 case 0x5c: +1261 case 0x5d: +1262 case 0x5e: +1263 case 0x5f: { // pop stack into r32 +1264 const uint8_t reg = op & 0x7; +1265 trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end(); +1266 //? cerr << "pop from " << Reg[ESP].u << '\n'; +1267 Reg[reg].u = pop(); +1268 //? cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n'; +1269 break; +1270 } +1271 :(code) +1272 uint32_t pop() { +1273 const uint32_t result = read_mem_u32(Reg[ESP].u); +1274 trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end(); +1275 Reg[ESP].u += 4; +1276 trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); +1277 assert(Reg[ESP].u < AFTER_STACK); +1278 return result; +1279 } diff --git a/html/subx/014indirect_addressing.cc.html b/html/subx/014indirect_addressing.cc.html index 35e6c0f6..d10849d9 100644 --- a/html/subx/014indirect_addressing.cc.html +++ b/html/subx/014indirect_addressing.cc.html @@ -3,8 +3,8 @@ Mu - subx/014indirect_addressing.cc - - + + @@ -16,11 +16,11 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } .Constant { color: #008787; } -.Delimiter { color: #c000c0; } +.Comment { color: #005faf; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Delimiter { color: #c000c0; } .SalientComment { color: #0000af; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -57,1001 +57,1006 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/014indirect_addressing.cc
-  1 //: operating on memory at the address provided by some register
-  2 //: we'll now start providing data in a separate segment
-  3 
-  4 void test_add_r32_to_mem_at_r32() {
-  5   Reg[EBX].i = 0x10;
-  6   Reg[EAX].i = 0x2000;
-  7   run(
-  8       "== code 0x1\n"
-  9       // op     ModR/M  SIB   displacement  immediate
- 10       "  01     18                                    \n"  // add EBX to *EAX
- 11       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
- 12       "== data 0x2000\n"
- 13       "01 00 00 00\n"  // 0x00000001
- 14   );
- 15   CHECK_TRACE_CONTENTS(
- 16       "run: add EBX to r/m32\n"
- 17       "run: effective address is 0x00002000 (EAX)\n"
- 18       "run: storing 0x00000011\n"
- 19   );
- 20 }
- 21 
- 22 :(before "End Mod Special-cases(addr)")
- 23 case 0:  // indirect addressing
- 24   switch (rm) {
- 25   default:  // address in register
- 26     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end();
- 27     addr = Reg[rm].u;
- 28     break;
- 29   // End Mod 0 Special-cases(addr)
- 30   }
- 31   break;
- 32 
- 33 //:
- 34 
- 35 :(before "End Initialize Op Names")
- 36 put_new(Name, "03", "add rm32 to r32 (add)");
- 37 
- 38 :(code)
- 39 void test_add_mem_at_r32_to_r32() {
- 40   Reg[EAX].i = 0x2000;
- 41   Reg[EBX].i = 0x10;
- 42   run(
- 43       "== code 0x1\n"
- 44       // op     ModR/M  SIB   displacement  immediate
- 45       "  03     18                                    \n"  // add *EAX to EBX
- 46       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
- 47       "== data 0x2000\n"
- 48       "01 00 00 00\n"  // 0x00000001
- 49   );
- 50   CHECK_TRACE_CONTENTS(
- 51       "run: add r/m32 to EBX\n"
- 52       "run: effective address is 0x00002000 (EAX)\n"
- 53       "run: storing 0x00000011\n"
- 54   );
- 55 }
- 56 
- 57 :(before "End Single-Byte Opcodes")
- 58 case 0x03: {  // add r/m32 to r32
- 59   const uint8_t modrm = next();
- 60   const uint8_t arg1 = (modrm>>3)&0x7;
- 61   trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end();
- 62   const int32_t* signed_arg2 = effective_address(modrm);
- 63   int32_t signed_result = Reg[arg1].i + *signed_arg2;
- 64   SF = (signed_result < 0);
- 65   ZF = (signed_result == 0);
- 66   int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) + *signed_arg2;
- 67   OF = (signed_result != signed_full_result);
- 68   // set CF
- 69   uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
- 70   uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2;
- 71   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) + unsigned_arg2;
- 72   CF = (unsigned_result != unsigned_full_result);
- 73   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
- 74   Reg[arg1].i = signed_result;
- 75   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
- 76   break;
- 77 }
- 78 
- 79 :(code)
- 80 void test_add_mem_at_r32_to_r32_signed_overflow() {
- 81   Reg[EAX].i = 0x2000;
- 82   Reg[EBX].i = 0x7fffffff;  // largest positive signed integer
- 83   run(
- 84       "== code 0x1\n"
- 85       // op     ModR/M  SIB   displacement  immediate
- 86       "  03     18                                    \n" // add *EAX to EBX
- 87       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
- 88       "== data 0x2000\n"
- 89       "01 00 00 00\n"  // 1
- 90   );
- 91   CHECK_TRACE_CONTENTS(
- 92       "run: add r/m32 to EBX\n"
- 93       "run: effective address is 0x00002000 (EAX)\n"
- 94       "run: effective address contains 1\n"
- 95       "run: SF=1; ZF=0; CF=0; OF=1\n"
- 96       "run: storing 0x80000000\n"
- 97   );
- 98 }
- 99 
-100 void test_add_mem_at_r32_to_r32_unsigned_overflow() {
-101   Reg[EAX].u = 0x2000;
-102   Reg[EBX].u = 0xffffffff;  // largest unsigned number
-103   run(
-104       "== code 0x1\n"
-105       // op     ModR/M  SIB   displacement  immediate
-106       "  03     18                                    \n" // add *EAX to EBX
-107       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-108       "== data 0x2000\n"
-109       "01 00 00 00\n"
-110   );
-111   CHECK_TRACE_CONTENTS(
-112       "run: add r/m32 to EBX\n"
-113       "run: effective address is 0x00002000 (EAX)\n"
-114       "run: effective address contains 1\n"
-115       "run: SF=0; ZF=1; CF=1; OF=0\n"
-116       "run: storing 0x00000000\n"
-117   );
-118 }
-119 
-120 void test_add_mem_at_r32_to_r32_unsigned_and_signed_overflow() {
-121   Reg[EAX].u = 0x2000;
-122   Reg[EBX].u = 0x80000000;  // smallest negative signed integer
-123   run(
-124       "== code 0x1\n"
-125       // op     ModR/M  SIB   displacement  immediate
-126       "  03     18                                    \n" // add *EAX to EBX
-127       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-128       "== data 0x2000\n"
-129       "00 00 00 80\n"  // smallest negative signed integer
-130   );
-131   CHECK_TRACE_CONTENTS(
-132       "run: add r/m32 to EBX\n"
-133       "run: effective address is 0x00002000 (EAX)\n"
-134       "run: effective address contains 80000000\n"
-135       "run: SF=0; ZF=1; CF=1; OF=1\n"
-136       "run: storing 0x00000000\n"
-137   );
-138 }
-139 
-140 //:: subtract
-141 
-142 :(code)
-143 void test_subtract_r32_from_mem_at_r32() {
-144   Reg[EAX].i = 0x2000;
-145   Reg[EBX].i = 1;
-146   run(
-147       "== code 0x1\n"
-148       // op     ModR/M  SIB   displacement  immediate
-149       "  29     18                                    \n"  // subtract EBX from *EAX
-150       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-151       "== data 0x2000\n"
-152       "0a 00 00 00\n"  // 0x0000000a
-153   );
-154   CHECK_TRACE_CONTENTS(
-155       "run: subtract EBX from r/m32\n"
-156       "run: effective address is 0x00002000 (EAX)\n"
-157       "run: storing 0x00000009\n"
-158   );
-159 }
-160 
-161 //:
-162 
-163 :(before "End Initialize Op Names")
-164 put_new(Name, "2b", "subtract rm32 from r32 (sub)");
-165 
-166 :(code)
-167 void test_subtract_mem_at_r32_from_r32() {
-168   Reg[EAX].i = 0x2000;
-169   Reg[EBX].i = 10;
-170   run(
-171       "== code 0x1\n"
-172       // op     ModR/M  SIB   displacement  immediate
-173       "  2b     18                                    \n"  // subtract *EAX from EBX
-174       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-175       "== data 0x2000\n"
-176       "01 00 00 00\n"  // 0x00000001
-177   );
-178   CHECK_TRACE_CONTENTS(
-179       "run: subtract r/m32 from EBX\n"
-180       "run: effective address is 0x00002000 (EAX)\n"
-181       "run: storing 0x00000009\n"
-182   );
-183 }
-184 
-185 :(before "End Single-Byte Opcodes")
-186 case 0x2b: {  // subtract r/m32 from r32
-187   const uint8_t modrm = next();
-188   const uint8_t arg1 = (modrm>>3)&0x7;
-189   trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end();
-190   const int32_t* signed_arg2 = effective_address(modrm);
-191   const int32_t signed_result = Reg[arg1].i - *signed_arg2;
-192   SF = (signed_result < 0);
-193   ZF = (signed_result == 0);
-194   int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) - *signed_arg2;
-195   OF = (signed_result != signed_full_result);
-196   // set CF
-197   uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
-198   uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2;
-199   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) - unsigned_arg2;
-200   CF = (unsigned_result != unsigned_full_result);
-201   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
-202   Reg[arg1].i = signed_result;
-203   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
-204   break;
-205 }
-206 
-207 :(code)
-208 void test_subtract_mem_at_r32_from_r32_signed_overflow() {
-209   Reg[EAX].i = 0x2000;
-210   Reg[EBX].i = 0x80000000;  // smallest negative signed integer
-211   run(
-212       "== code 0x1\n"
-213       // op     ModR/M  SIB   displacement  immediate
-214       "  2b     18                                    \n"  // subtract *EAX from EBX
-215       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-216       "== data 0x2000\n"
-217       "ff ff ff 7f\n"  // largest positive signed integer
-218   );
-219   CHECK_TRACE_CONTENTS(
-220       "run: subtract r/m32 from EBX\n"
-221       "run: effective address is 0x00002000 (EAX)\n"
-222       "run: effective address contains 7fffffff\n"
-223       "run: SF=0; ZF=0; CF=0; OF=1\n"
-224       "run: storing 0x00000001\n"
-225   );
-226 }
-227 
-228 void test_subtract_mem_at_r32_from_r32_unsigned_overflow() {
-229   Reg[EAX].i = 0x2000;
-230   Reg[EBX].i = 0;
-231   run(
-232       "== code 0x1\n"
-233       // op     ModR/M  SIB   displacement  immediate
-234       "  2b     18                                    \n"  // subtract *EAX from EBX
-235       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-236       "== data 0x2000\n"
-237       "01 00 00 00\n"  // 1
-238   );
-239   CHECK_TRACE_CONTENTS(
-240       "run: subtract r/m32 from EBX\n"
-241       "run: effective address is 0x00002000 (EAX)\n"
-242       "run: effective address contains 1\n"
-243       "run: SF=1; ZF=0; CF=1; OF=0\n"
-244       "run: storing 0xffffffff\n"
-245   );
-246 }
-247 
-248 void test_subtract_mem_at_r32_from_r32_signed_and_unsigned_overflow() {
-249   Reg[EAX].i = 0x2000;
-250   Reg[EBX].i = 0;
-251   run(
-252       "== code 0x1\n"
-253       // op     ModR/M  SIB   displacement  immediate
-254       "  2b     18                                    \n"  // subtract *EAX from EBX
-255       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-256       "== data 0x2000\n"
-257       "00 00 00 80\n"  // smallest negative signed integer
-258   );
-259   CHECK_TRACE_CONTENTS(
-260       "run: subtract r/m32 from EBX\n"
-261       "run: effective address is 0x00002000 (EAX)\n"
-262       "run: effective address contains 80000000\n"
-263       "run: SF=1; ZF=0; CF=1; OF=1\n"
-264       "run: storing 0x80000000\n"
-265   );
-266 }
-267 
-268 //:: and
-269 :(code)
-270 void test_and_r32_with_mem_at_r32() {
-271   Reg[EAX].i = 0x2000;
-272   Reg[EBX].i = 0xff;
-273   run(
-274       "== code 0x1\n"
-275       // op     ModR/M  SIB   displacement  immediate
-276       "  21     18                                    \n"  // and EBX with *EAX
-277       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-278       "== data 0x2000\n"
-279       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-280   );
-281   CHECK_TRACE_CONTENTS(
-282       "run: and EBX with r/m32\n"
-283       "run: effective address is 0x00002000 (EAX)\n"
-284       "run: storing 0x0000000d\n"
-285   );
-286 }
-287 
-288 //:
-289 
-290 :(before "End Initialize Op Names")
-291 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)");
-292 
-293 :(code)
-294 void test_and_mem_at_r32_with_r32() {
-295   Reg[EAX].i = 0x2000;
-296   Reg[EBX].i = 0x0a0b0c0d;
-297   run(
-298       "== code 0x1\n"
-299       // op     ModR/M  SIB   displacement  immediate
-300       "  23     18                                    \n"  // and *EAX with EBX
-301       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-302       "== data 0x2000\n"
-303       "ff 00 00 00\n"  // 0x000000ff
-304   );
-305   CHECK_TRACE_CONTENTS(
-306       "run: and r/m32 with EBX\n"
-307       "run: effective address is 0x00002000 (EAX)\n"
-308       "run: storing 0x0000000d\n"
-309   );
-310 }
-311 
-312 :(before "End Single-Byte Opcodes")
-313 case 0x23: {  // and r/m32 with r32
-314   const uint8_t modrm = next();
-315   const uint8_t arg1 = (modrm>>3)&0x7;
-316   trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end();
-317   // bitwise ops technically operate on unsigned numbers, but it makes no
-318   // difference
-319   const int32_t* signed_arg2 = effective_address(modrm);
-320   Reg[arg1].i &= *signed_arg2;
-321   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
-322   SF = (Reg[arg1].i >> 31);
-323   ZF = (Reg[arg1].i == 0);
-324   CF = false;
-325   OF = false;
-326   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
-327   break;
-328 }
-329 
-330 //:: or
-331 
-332 :(code)
-333 void test_or_r32_with_mem_at_r32() {
-334   Reg[EAX].i = 0x2000;
-335   Reg[EBX].i = 0xa0b0c0d0;
-336   run(
-337       "== code 0x1\n"
-338       // op     ModR/M  SIB   displacement  immediate
-339       "  09     18                                   #\n"  // EBX with *EAX
-340       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-341       "== data 0x2000\n"
-342       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-343   );
-344   CHECK_TRACE_CONTENTS(
-345       "run: or EBX with r/m32\n"
-346       "run: effective address is 0x00002000 (EAX)\n"
-347       "run: storing 0xaabbccdd\n"
-348   );
-349 }
-350 
-351 //:
-352 
-353 :(before "End Initialize Op Names")
-354 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
-355 
-356 :(code)
-357 void test_or_mem_at_r32_with_r32() {
-358   Reg[EAX].i = 0x2000;
-359   Reg[EBX].i = 0xa0b0c0d0;
-360   run(
-361       "== code 0x1\n"
-362       // op     ModR/M  SIB   displacement  immediate
-363       "  0b     18                                    \n"  // or *EAX with EBX
-364       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-365       "== data 0x2000\n"
-366       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-367   );
-368   CHECK_TRACE_CONTENTS(
-369       "run: or r/m32 with EBX\n"
-370       "run: effective address is 0x00002000 (EAX)\n"
-371       "run: storing 0xaabbccdd\n"
-372   );
-373 }
-374 
-375 :(before "End Single-Byte Opcodes")
-376 case 0x0b: {  // or r/m32 with r32
-377   const uint8_t modrm = next();
-378   const uint8_t arg1 = (modrm>>3)&0x7;
-379   trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end();
-380   // bitwise ops technically operate on unsigned numbers, but it makes no
-381   // difference
-382   const int32_t* signed_arg2 = effective_address(modrm);
-383   Reg[arg1].i |= *signed_arg2;
-384   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
-385   SF = (Reg[arg1].i >> 31);
-386   ZF = (Reg[arg1].i == 0);
-387   CF = false;
-388   OF = false;
-389   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
-390   break;
-391 }
-392 
-393 //:: xor
-394 
-395 :(code)
-396 void test_xor_r32_with_mem_at_r32() {
-397   Reg[EAX].i = 0x2000;
-398   Reg[EBX].i = 0xa0b0c0d0;
-399   run(
-400       "== code 0x1\n"
-401       // op     ModR/M  SIB   displacement  immediate
-402       "  31     18                                    \n"  // xor EBX with *EAX
-403       "== data 0x2000\n"
-404       "0d 0c bb aa\n"  // 0xaabb0c0d
-405   );
-406   CHECK_TRACE_CONTENTS(
-407       "run: xor EBX with r/m32\n"
-408       "run: effective address is 0x00002000 (EAX)\n"
-409       "run: storing 0x0a0bccdd\n"
-410   );
-411 }
-412 
-413 //:
-414 
-415 :(before "End Initialize Op Names")
-416 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
-417 
-418 :(code)
-419 void test_xor_mem_at_r32_with_r32() {
-420   Reg[EAX].i = 0x2000;
-421   Reg[EBX].i = 0xa0b0c0d0;
-422   run(
-423       "== code 0x1\n"
-424       // op     ModR/M  SIB   displacement  immediate
-425       "  33     18                                    \n"  // xor *EAX with EBX
-426       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-427       "== data 0x2000\n"
-428       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-429   );
-430   CHECK_TRACE_CONTENTS(
-431       "run: xor r/m32 with EBX\n"
-432       "run: effective address is 0x00002000 (EAX)\n"
-433       "run: storing 0xaabbccdd\n"
-434   );
-435 }
-436 
-437 :(before "End Single-Byte Opcodes")
-438 case 0x33: {  // xor r/m32 with r32
-439   const uint8_t modrm = next();
-440   const uint8_t arg1 = (modrm>>3)&0x7;
-441   trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end();
-442   // bitwise ops technically operate on unsigned numbers, but it makes no
-443   // difference
-444   const int32_t* signed_arg2 = effective_address(modrm);
-445   Reg[arg1].i |= *signed_arg2;
-446   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
-447   SF = (Reg[arg1].i >> 31);
-448   ZF = (Reg[arg1].i == 0);
-449   CF = false;
-450   OF = false;
-451   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
-452   break;
-453 }
-454 
-455 //:: not
-456 
-457 :(code)
-458 void test_not_of_mem_at_r32() {
-459   Reg[EBX].i = 0x2000;
-460   run(
-461       "== code 0x1\n"
-462       // op     ModR/M  SIB   displacement  immediate
-463       "  f7     13                                    \n"  // not *EBX
-464       // ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX)
-465       "== data 0x2000\n"
-466       "ff 00 0f 0f\n"  // 0x0f0f00ff
-467   );
-468   CHECK_TRACE_CONTENTS(
-469       "run: operate on r/m32\n"
-470       "run: effective address is 0x00002000 (EBX)\n"
-471       "run: subop: not\n"
-472       "run: storing 0xf0f0ff00\n"
-473   );
-474 }
-475 
-476 //:: compare (cmp)
-477 
-478 :(code)
-479 void test_compare_mem_at_r32_with_r32_greater() {
-480   Reg[EAX].i = 0x2000;
-481   Reg[EBX].i = 0x0a0b0c07;
-482   run(
-483       "== code 0x1\n"
-484       // op     ModR/M  SIB   displacement  immediate
-485       "  39     18                                    \n"  // compare *EAX with EBX
-486       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-487       "== data 0x2000\n"
-488       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-489   );
-490   CHECK_TRACE_CONTENTS(
-491       "run: compare r/m32 with EBX\n"
-492       "run: effective address is 0x00002000 (EAX)\n"
-493       "run: SF=0; ZF=0; CF=0; OF=0\n"
-494   );
-495 }
-496 
-497 :(code)
-498 void test_compare_mem_at_r32_with_r32_lesser() {
-499   Reg[EAX].i = 0x2000;
-500   Reg[EBX].i = 0x0a0b0c0d;
-501   run(
-502       "== code 0x1\n"
-503       // op     ModR/M  SIB   displacement  immediate
-504       "  39     18                                    \n"  // compare *EAX with EBX
-505       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-506       "== data 0x2000\n"
-507       "07 0c 0b 0a\n"  // 0x0a0b0c0d
-508   );
-509   CHECK_TRACE_CONTENTS(
-510       "run: compare r/m32 with EBX\n"
-511       "run: effective address is 0x00002000 (EAX)\n"
-512       "run: SF=1; ZF=0; CF=1; OF=0\n"
-513   );
-514 }
-515 
-516 :(code)
-517 void test_compare_mem_at_r32_with_r32_equal() {
-518   Reg[EAX].i = 0x2000;
-519   Reg[EBX].i = 0x0a0b0c0d;
-520   run(
-521       "== code 0x1\n"
-522       // op     ModR/M  SIB   displacement  immediate
-523       "  39     18                                    \n"  // compare *EAX and EBX
-524       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-525       "== data 0x2000\n"
-526       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-527   );
-528   CHECK_TRACE_CONTENTS(
-529       "run: compare r/m32 with EBX\n"
-530       "run: effective address is 0x00002000 (EAX)\n"
-531       "run: SF=0; ZF=1; CF=0; OF=0\n"
-532   );
-533 }
-534 
-535 //:
-536 
-537 :(before "End Initialize Op Names")
-538 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
-539 
-540 :(code)
-541 void test_compare_r32_with_mem_at_r32_greater() {
-542   Reg[EAX].i = 0x2000;
-543   Reg[EBX].i = 0x0a0b0c0d;
-544   run(
-545       "== code 0x1\n"
-546       // op     ModR/M  SIB   displacement  immediate
-547       "  3b     18                                    \n"  // compare EBX with *EAX
-548       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-549       "== data 0x2000\n"
-550       "07 0c 0b 0a\n"  // 0x0a0b0c07
-551   );
-552   CHECK_TRACE_CONTENTS(
-553       "run: compare EBX with r/m32\n"
-554       "run: effective address is 0x00002000 (EAX)\n"
-555       "run: SF=0; ZF=0; CF=0; OF=0\n"
-556   );
-557 }
-558 
-559 :(before "End Single-Byte Opcodes")
-560 case 0x3b: {  // set SF if r32 < r/m32
-561   const uint8_t modrm = next();
-562   const uint8_t reg1 = (modrm>>3)&0x7;
-563   trace(Callstack_depth+1, "run") << "compare " << rname(reg1) << " with r/m32" << end();
-564   const int32_t* signed_arg2 = effective_address(modrm);
-565   const int32_t signed_difference = Reg[reg1].i - *signed_arg2;
-566   SF = (signed_difference < 0);
-567   ZF = (signed_difference == 0);
-568   int64_t full_signed_difference = static_cast<int64_t>(Reg[reg1].i) - *signed_arg2;
-569   OF = (signed_difference != full_signed_difference);
-570   const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
-571   const uint32_t unsigned_difference = Reg[reg1].u - unsigned_arg2;
-572   const uint64_t full_unsigned_difference = static_cast<uint64_t>(Reg[reg1].u) - unsigned_arg2;
-573   CF = (unsigned_difference != full_unsigned_difference);
-574   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
-575   break;
-576 }
-577 
-578 :(code)
-579 void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed() {
-580   Reg[EAX].i = 0x2000;
-581   Reg[EBX].i = 0x0a0b0c07;
-582   run(
-583       "== code 0x1\n"
-584       // op     ModR/M  SIB   displacement  immediate
-585       "  3b     18                                    \n"  // compare EBX with *EAX
-586       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-587       "== data 0x2000\n"
-588       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-589   );
-590   CHECK_TRACE_CONTENTS(
-591       "run: compare EBX with r/m32\n"
-592       "run: effective address is 0x00002000 (EAX)\n"
-593       "run: effective address contains a0b0c0d\n"
-594       "run: SF=1; ZF=0; CF=1; OF=0\n"
-595   );
-596 }
-597 
-598 void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed_due_to_overflow() {
-599   Reg[EAX].i = 0x2000;
-600   Reg[EBX].i = 0x7fffffff;  // largest positive signed integer
-601   run(
-602       "== code 0x1\n"
-603       // op     ModR/M  SIB   displacement  immediate
-604       "  3b     18                                    \n"  // compare EBX with *EAX
-605       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-606       "== data 0x2000\n"
-607       "00 00 00 80\n"  // smallest negative signed integer
-608   );
-609   CHECK_TRACE_CONTENTS(
-610       "run: compare EBX with r/m32\n"
-611       "run: effective address is 0x00002000 (EAX)\n"
-612       "run: effective address contains 80000000\n"
-613       "run: SF=1; ZF=0; CF=1; OF=1\n"
-614   );
-615 }
-616 
-617 void test_compare_r32_with_mem_at_r32_lesser_signed() {
-618   Reg[EAX].i = 0x2000;
-619   Reg[EBX].i = 0xffffffff;  // -1
-620   run(
-621       "== code 0x1\n"
-622       // op     ModR/M  SIB   displacement  immediate
-623       "  3b     18                                    \n"  // compare EBX with *EAX
-624       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-625       "== data 0x2000\n"
-626       "01 00 00 00\n"  // 1
-627   );
-628   CHECK_TRACE_CONTENTS(
-629       "run: compare EBX with r/m32\n"
-630       "run: effective address is 0x00002000 (EAX)\n"
-631       "run: effective address contains 1\n"
-632       "run: SF=1; ZF=0; CF=0; OF=0\n"
-633   );
-634 }
-635 
-636 void test_compare_r32_with_mem_at_r32_lesser_unsigned() {
-637   Reg[EAX].i = 0x2000;
-638   Reg[EBX].i = 0x00000001;  // 1
-639   run(
-640       "== code 0x1\n"
-641       // op     ModR/M  SIB   displacement  immediate
-642       "  3b     18                                    \n"  // compare EBX with *EAX
-643       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
-644       "== data 0x2000\n"
-645       "ff ff ff ff\n"  // -1
-646   );
-647   CHECK_TRACE_CONTENTS(
-648       "run: compare EBX with r/m32\n"
-649       "run: effective address is 0x00002000 (EAX)\n"
-650       "run: effective address contains ffffffff\n"
-651       "run: SF=0; ZF=0; CF=1; OF=0\n"
-652   );
-653 }
-654 
-655 void test_compare_r32_with_mem_at_r32_equal() {
-656   Reg[EAX].i = 0x2000;
-657   Reg[EBX].i = 0x0a0b0c0d;
-658   run(
-659       "== code 0x1\n"
-660       // op     ModR/M  SIB   displacement  immediate
-661       "  3b     18                                    \n"  // compare EBX with *EAX
-662       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-663       "== data 0x2000\n"
-664       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
-665   );
-666   CHECK_TRACE_CONTENTS(
-667       "run: compare EBX with r/m32\n"
-668       "run: effective address is 0x00002000 (EAX)\n"
-669       "run: SF=0; ZF=1; CF=0; OF=0\n"
-670   );
-671 }
-672 
-673 //:: copy (mov)
-674 
-675 void test_copy_r32_to_mem_at_r32() {
-676   Reg[EBX].i = 0xaf;
-677   Reg[EAX].i = 0x60;
-678   run(
-679       "== code 0x1\n"
-680       // op     ModR/M  SIB   displacement  immediate
-681       "  89     18                                    \n"  // copy EBX to *EAX
-682       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
-683   );
-684   CHECK_TRACE_CONTENTS(
-685       "run: copy EBX to r/m32\n"
-686       "run: effective address is 0x00000060 (EAX)\n"
-687       "run: storing 0x000000af\n"
-688   );
-689 }
-690 
-691 //:
-692 
-693 :(before "End Initialize Op Names")
-694 put_new(Name, "8b", "copy rm32 to r32 (mov)");
-695 
-696 :(code)
-697 void test_copy_mem_at_r32_to_r32() {
-698   Reg[EAX].i = 0x2000;
-699   run(
-700       "== code 0x1\n"
-701       // op     ModR/M  SIB   displacement  immediate
-702       "  8b     18                                    \n"  // copy *EAX to EBX
-703       "== data 0x2000\n"
-704       "af 00 00 00\n"  // 0x000000af
-705   );
-706   CHECK_TRACE_CONTENTS(
-707       "run: copy r/m32 to EBX\n"
-708       "run: effective address is 0x00002000 (EAX)\n"
-709       "run: storing 0x000000af\n"
-710   );
-711 }
-712 
-713 :(before "End Single-Byte Opcodes")
-714 case 0x8b: {  // copy r32 to r/m32
-715   const uint8_t modrm = next();
-716   const uint8_t rdest = (modrm>>3)&0x7;
-717   trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end();
-718   const int32_t* src = effective_address(modrm);
-719   Reg[rdest].i = *src;
-720   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end();
-721   break;
-722 }
-723 
-724 //:: jump
-725 
-726 :(code)
-727 void test_jump_mem_at_r32() {
-728   Reg[EAX].i = 0x2000;
-729   run(
-730       "== code 0x1\n"
-731       // op     ModR/M  SIB   displacement  immediate
-732       "  ff     20                                    \n"  // jump to *EAX
-733       // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX)
-734       "  b8                                 00 00 00 01\n"
-735       "  b8                                 00 00 00 02\n"
-736       "== data 0x2000\n"
-737       "08 00 00 00\n"  // 0x00000008
-738   );
-739   CHECK_TRACE_CONTENTS(
-740       "run: 0x00000001 opcode: ff\n"
-741       "run: jump to r/m32\n"
-742       "run: effective address is 0x00002000 (EAX)\n"
-743       "run: jumping to 0x00000008\n"
-744       "run: 0x00000008 opcode: b8\n"
-745   );
-746   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: b8");
-747 }
-748 
-749 :(before "End Op ff Subops")
-750 case 4: {  // jump to r/m32
-751   trace(Callstack_depth+1, "run") << "jump to r/m32" << end();
-752   const int32_t* arg2 = effective_address(modrm);
-753   EIP = *arg2;
-754   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
-755   break;
-756 }
-757 
-758 //:: push
-759 
-760 :(code)
-761 void test_push_mem_at_r32() {
-762   Reg[EAX].i = 0x2000;
-763   Mem.push_back(vma(0xbd000000));  // manually allocate memory
-764   Reg[ESP].u = 0xbd000014;
-765   run(
-766       "== code 0x1\n"
-767       // op     ModR/M  SIB   displacement  immediate
-768       "  ff     30                                    \n"  // push *EAX to stack
-769       "== data 0x2000\n"
-770       "af 00 00 00\n"  // 0x000000af
-771   );
-772   CHECK_TRACE_CONTENTS(
-773       "run: push r/m32\n"
-774       "run: effective address is 0x00002000 (EAX)\n"
-775       "run: decrementing ESP to 0xbd000010\n"
-776       "run: pushing value 0x000000af\n"
-777   );
-778 }
-779 
-780 :(before "End Op ff Subops")
-781 case 6: {  // push r/m32 to stack
-782   trace(Callstack_depth+1, "run") << "push r/m32" << end();
-783   const int32_t* val = effective_address(modrm);
-784   push(*val);
-785   break;
-786 }
-787 
-788 //:: pop
-789 
-790 :(before "End Initialize Op Names")
-791 put_new(Name, "8f", "pop top of stack to rm32 (pop)");
-792 
-793 :(code)
-794 void test_pop_mem_at_r32() {
-795   Reg[EAX].i = 0x60;
-796   Mem.push_back(vma(0xbd000000));  // manually allocate memory
-797   Reg[ESP].u = 0xbd000000;
-798   write_mem_i32(0xbd000000, 0x00000030);
-799   run(
-800       "== code 0x1\n"
-801       // op     ModR/M  SIB   displacement  immediate
-802       "  8f     00                                    \n"  // pop stack into *EAX
-803       // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX)
-804   );
-805   CHECK_TRACE_CONTENTS(
-806       "run: pop into r/m32\n"
-807       "run: effective address is 0x00000060 (EAX)\n"
-808       "run: popping value 0x00000030\n"
-809       "run: incrementing ESP to 0xbd000004\n"
-810   );
-811 }
-812 
-813 :(before "End Single-Byte Opcodes")
-814 case 0x8f: {  // pop stack into r/m32
-815   const uint8_t modrm = next();
-816   const uint8_t subop = (modrm>>3)&0x7;
-817   switch (subop) {
-818     case 0: {
-819       trace(Callstack_depth+1, "run") << "pop into r/m32" << end();
-820       int32_t* dest = effective_address(modrm);
-821       *dest = pop();
-822       break;
-823     }
-824   }
-825   break;
-826 }
-827 
-828 //:: special-case for loading address from disp32 rather than register
-829 
-830 :(code)
-831 void test_add_r32_to_mem_at_displacement() {
-832   Reg[EBX].i = 0x10;  // source
-833   run(
-834       "== code 0x1\n"
-835       // op     ModR/M  SIB   displacement  immediate
-836       "  01     1d            00 20 00 00             \n"  // add EBX to *0x2000
-837       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32)
-838       "== data 0x2000\n"
-839       "01 00 00 00\n"  // 0x00000001
-840   );
-841   CHECK_TRACE_CONTENTS(
-842       "run: add EBX to r/m32\n"
-843       "run: effective address is 0x00002000 (disp32)\n"
-844       "run: storing 0x00000011\n"
-845   );
-846 }
-847 
-848 :(before "End Mod 0 Special-cases(addr)")
-849 case 5:  // exception: mod 0b00 rm 0b101 => incoming disp32
-850   addr = next32();
-851   trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
-852   break;
-853 
-854 //:
-855 
-856 :(code)
-857 void test_add_r32_to_mem_at_r32_plus_disp8() {
-858   Reg[EBX].i = 0x10;  // source
-859   Reg[EAX].i = 0x1ffe;  // dest
-860   run(
-861       "== code 0x1\n"
-862       // op     ModR/M  SIB   displacement  immediate
-863       "  01     58            02                      \n"  // add EBX to *(EAX+2)
-864       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
-865       "== data 0x2000\n"
-866       "01 00 00 00\n"  // 0x00000001
-867   );
-868   CHECK_TRACE_CONTENTS(
-869       "run: add EBX to r/m32\n"
-870       "run: effective address is initially 0x00001ffe (EAX)\n"
-871       "run: effective address is 0x00002000 (after adding disp8)\n"
-872       "run: storing 0x00000011\n"
-873   );
-874 }
-875 
-876 :(before "End Mod Special-cases(addr)")
-877 case 1:  // indirect + disp8 addressing
-878   switch (rm) {
-879   default:
-880     addr = Reg[rm].u;
-881     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
-882     break;
-883   // End Mod 1 Special-cases(addr)
-884   }
-885   if (addr > 0) {
-886     addr += static_cast<int8_t>(next());
-887     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
-888   }
-889   break;
-890 
-891 :(code)
-892 void test_add_r32_to_mem_at_r32_plus_negative_disp8() {
-893   Reg[EBX].i = 0x10;  // source
-894   Reg[EAX].i = 0x2001;  // dest
-895   run(
-896       "== code 0x1\n"
-897       // op     ModR/M  SIB   displacement  immediate
-898       "  01     58            ff                      \n"  // add EBX to *(EAX-1)
-899       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
-900       "== data 0x2000\n"
-901       "01 00 00 00\n"  // 0x00000001
-902   );
-903   CHECK_TRACE_CONTENTS(
-904       "run: add EBX to r/m32\n"
-905       "run: effective address is initially 0x00002001 (EAX)\n"
-906       "run: effective address is 0x00002000 (after adding disp8)\n"
-907       "run: storing 0x00000011\n"
-908   );
-909 }
-910 
-911 //:
-912 
-913 :(code)
-914 void test_add_r32_to_mem_at_r32_plus_disp32() {
-915   Reg[EBX].i = 0x10;  // source
-916   Reg[EAX].i = 0x1ffe;  // dest
-917   run(
-918       "== code 0x1\n"
-919       // op     ModR/M  SIB   displacement  immediate
-920       "  01     98            02 00 00 00             \n"  // add EBX to *(EAX+2)
-921       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
-922       "== data 0x2000\n"
-923       "01 00 00 00\n"  // 0x00000001
-924   );
-925   CHECK_TRACE_CONTENTS(
-926       "run: add EBX to r/m32\n"
-927       "run: effective address is initially 0x00001ffe (EAX)\n"
-928       "run: effective address is 0x00002000 (after adding disp32)\n"
-929       "run: storing 0x00000011\n"
-930   );
-931 }
-932 
-933 :(before "End Mod Special-cases(addr)")
-934 case 2:  // indirect + disp32 addressing
-935   switch (rm) {
-936   default:
-937     addr = Reg[rm].u;
-938     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
-939     break;
-940   // End Mod 2 Special-cases(addr)
-941   }
-942   if (addr > 0) {
-943     addr += next32();
-944     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
-945   }
-946   break;
-947 
-948 :(code)
-949 void test_add_r32_to_mem_at_r32_plus_negative_disp32() {
-950   Reg[EBX].i = 0x10;  // source
-951   Reg[EAX].i = 0x2001;  // dest
-952   run(
-953       "== code 0x1\n"
-954       // op     ModR/M  SIB   displacement  immediate
-955       "  01     98            ff ff ff ff             \n"  // add EBX to *(EAX-1)
-956       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
-957       "== data 0x2000\n"
-958       "01 00 00 00\n"  // 0x00000001
-959   );
-960   CHECK_TRACE_CONTENTS(
-961       "run: add EBX to r/m32\n"
-962       "run: effective address is initially 0x00002001 (EAX)\n"
-963       "run: effective address is 0x00002000 (after adding disp32)\n"
-964       "run: storing 0x00000011\n"
-965   );
-966 }
-967 
-968 //:: copy address (lea)
-969 
-970 :(before "End Initialize Op Names")
-971 put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
-972 
-973 :(code)
-974 void test_copy_address() {
-975   Reg[EAX].u = 0x2000;
-976   run(
-977       "== code 0x1\n"
-978       // op     ModR/M  SIB   displacement  immediate
-979       "  8d     18                                    \n"  // copy address in EAX into EBX
-980       // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
-981   );
-982   CHECK_TRACE_CONTENTS(
-983       "run: copy address into EBX\n"
-984       "run: effective address is 0x00002000 (EAX)\n"
-985   );
-986 }
-987 
-988 :(before "End Single-Byte Opcodes")
-989 case 0x8d: {  // copy address of m32 to r32
-990   const uint8_t modrm = next();
-991   const uint8_t arg1 = (modrm>>3)&0x7;
-992   trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end();
-993   Reg[arg1].u = effective_address_number(modrm);
-994   break;
-995 }
+   1 //: operating on memory at the address provided by some register
+   2 //: we'll now start providing data in a separate segment
+   3 
+   4 void test_add_r32_to_mem_at_r32() {
+   5   Reg[EBX].i = 0x10;
+   6   Reg[EAX].i = 0x2000;
+   7   run(
+   8       "== code 0x1\n"
+   9       // op     ModR/M  SIB   displacement  immediate
+  10       "  01     18                                    \n"  // add EBX to *EAX
+  11       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+  12       "== data 0x2000\n"
+  13       "01 00 00 00\n"  // 0x00000001
+  14   );
+  15   CHECK_TRACE_CONTENTS(
+  16       "run: add EBX to r/m32\n"
+  17       "run: effective address is 0x00002000 (EAX)\n"
+  18       "run: storing 0x00000011\n"
+  19   );
+  20 }
+  21 
+  22 :(before "End Mod Special-cases(addr)")
+  23 case 0:  // indirect addressing
+  24   switch (rm) {
+  25   default:  // address in register
+  26     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end();
+  27     addr = Reg[rm].u;
+  28     break;
+  29   // End Mod 0 Special-cases(addr)
+  30   }
+  31   break;
+  32 
+  33 //:
+  34 
+  35 :(before "End Initialize Op Names")
+  36 put_new(Name, "03", "add rm32 to r32 (add)");
+  37 
+  38 :(code)
+  39 void test_add_mem_at_r32_to_r32() {
+  40   Reg[EAX].i = 0x2000;
+  41   Reg[EBX].i = 0x10;
+  42   run(
+  43       "== code 0x1\n"
+  44       // op     ModR/M  SIB   displacement  immediate
+  45       "  03     18                                    \n"  // add *EAX to EBX
+  46       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+  47       "== data 0x2000\n"
+  48       "01 00 00 00\n"  // 0x00000001
+  49   );
+  50   CHECK_TRACE_CONTENTS(
+  51       "run: add r/m32 to EBX\n"
+  52       "run: effective address is 0x00002000 (EAX)\n"
+  53       "run: storing 0x00000011\n"
+  54   );
+  55 }
+  56 
+  57 :(before "End Single-Byte Opcodes")
+  58 case 0x03: {  // add r/m32 to r32
+  59   const uint8_t modrm = next();
+  60   const uint8_t arg1 = (modrm>>3)&0x7;
+  61   trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end();
+  62   const int32_t* signed_arg2 = effective_address(modrm);
+  63   int32_t signed_result = Reg[arg1].i + *signed_arg2;
+  64   SF = (signed_result < 0);
+  65   ZF = (signed_result == 0);
+  66   int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) + *signed_arg2;
+  67   OF = (signed_result != signed_full_result);
+  68   // set CF
+  69   uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
+  70   uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2;
+  71   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) + unsigned_arg2;
+  72   CF = (unsigned_result != unsigned_full_result);
+  73   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+  74   Reg[arg1].i = signed_result;
+  75   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
+  76   break;
+  77 }
+  78 
+  79 :(code)
+  80 void test_add_mem_at_r32_to_r32_signed_overflow() {
+  81   Reg[EAX].i = 0x2000;
+  82   Reg[EBX].i = 0x7fffffff;  // largest positive signed integer
+  83   run(
+  84       "== code 0x1\n"
+  85       // op     ModR/M  SIB   displacement  immediate
+  86       "  03     18                                    \n" // add *EAX to EBX
+  87       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+  88       "== data 0x2000\n"
+  89       "01 00 00 00\n"  // 1
+  90   );
+  91   CHECK_TRACE_CONTENTS(
+  92       "run: add r/m32 to EBX\n"
+  93       "run: effective address is 0x00002000 (EAX)\n"
+  94       "run: effective address contains 1\n"
+  95       "run: SF=1; ZF=0; CF=0; OF=1\n"
+  96       "run: storing 0x80000000\n"
+  97   );
+  98 }
+  99 
+ 100 void test_add_mem_at_r32_to_r32_unsigned_overflow() {
+ 101   Reg[EAX].u = 0x2000;
+ 102   Reg[EBX].u = 0xffffffff;  // largest unsigned number
+ 103   run(
+ 104       "== code 0x1\n"
+ 105       // op     ModR/M  SIB   displacement  immediate
+ 106       "  03     18                                    \n" // add *EAX to EBX
+ 107       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 108       "== data 0x2000\n"
+ 109       "01 00 00 00\n"
+ 110   );
+ 111   CHECK_TRACE_CONTENTS(
+ 112       "run: add r/m32 to EBX\n"
+ 113       "run: effective address is 0x00002000 (EAX)\n"
+ 114       "run: effective address contains 1\n"
+ 115       "run: SF=0; ZF=1; CF=1; OF=0\n"
+ 116       "run: storing 0x00000000\n"
+ 117   );
+ 118 }
+ 119 
+ 120 void test_add_mem_at_r32_to_r32_unsigned_and_signed_overflow() {
+ 121   Reg[EAX].u = 0x2000;
+ 122   Reg[EBX].u = 0x80000000;  // smallest negative signed integer
+ 123   run(
+ 124       "== code 0x1\n"
+ 125       // op     ModR/M  SIB   displacement  immediate
+ 126       "  03     18                                    \n" // add *EAX to EBX
+ 127       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 128       "== data 0x2000\n"
+ 129       "00 00 00 80\n"  // smallest negative signed integer
+ 130   );
+ 131   CHECK_TRACE_CONTENTS(
+ 132       "run: add r/m32 to EBX\n"
+ 133       "run: effective address is 0x00002000 (EAX)\n"
+ 134       "run: effective address contains 80000000\n"
+ 135       "run: SF=0; ZF=1; CF=1; OF=1\n"
+ 136       "run: storing 0x00000000\n"
+ 137   );
+ 138 }
+ 139 
+ 140 //:: subtract
+ 141 
+ 142 :(code)
+ 143 void test_subtract_r32_from_mem_at_r32() {
+ 144   Reg[EAX].i = 0x2000;
+ 145   Reg[EBX].i = 1;
+ 146   run(
+ 147       "== code 0x1\n"
+ 148       // op     ModR/M  SIB   displacement  immediate
+ 149       "  29     18                                    \n"  // subtract EBX from *EAX
+ 150       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 151       "== data 0x2000\n"
+ 152       "0a 00 00 00\n"  // 0x0000000a
+ 153   );
+ 154   CHECK_TRACE_CONTENTS(
+ 155       "run: subtract EBX from r/m32\n"
+ 156       "run: effective address is 0x00002000 (EAX)\n"
+ 157       "run: storing 0x00000009\n"
+ 158   );
+ 159 }
+ 160 
+ 161 //:
+ 162 
+ 163 :(before "End Initialize Op Names")
+ 164 put_new(Name, "2b", "subtract rm32 from r32 (sub)");
+ 165 
+ 166 :(code)
+ 167 void test_subtract_mem_at_r32_from_r32() {
+ 168   Reg[EAX].i = 0x2000;
+ 169   Reg[EBX].i = 10;
+ 170   run(
+ 171       "== code 0x1\n"
+ 172       // op     ModR/M  SIB   displacement  immediate
+ 173       "  2b     18                                    \n"  // subtract *EAX from EBX
+ 174       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 175       "== data 0x2000\n"
+ 176       "01 00 00 00\n"  // 0x00000001
+ 177   );
+ 178   CHECK_TRACE_CONTENTS(
+ 179       "run: subtract r/m32 from EBX\n"
+ 180       "run: effective address is 0x00002000 (EAX)\n"
+ 181       "run: storing 0x00000009\n"
+ 182   );
+ 183 }
+ 184 
+ 185 :(before "End Single-Byte Opcodes")
+ 186 case 0x2b: {  // subtract r/m32 from r32
+ 187   const uint8_t modrm = next();
+ 188   const uint8_t arg1 = (modrm>>3)&0x7;
+ 189   trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end();
+ 190   const int32_t* signed_arg2 = effective_address(modrm);
+ 191   const int32_t signed_result = Reg[arg1].i - *signed_arg2;
+ 192   SF = (signed_result < 0);
+ 193   ZF = (signed_result == 0);
+ 194   int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) - *signed_arg2;
+ 195   OF = (signed_result != signed_full_result);
+ 196   // set CF
+ 197   uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
+ 198   uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2;
+ 199   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) - unsigned_arg2;
+ 200   CF = (unsigned_result != unsigned_full_result);
+ 201   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 202   Reg[arg1].i = signed_result;
+ 203   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
+ 204   break;
+ 205 }
+ 206 
+ 207 :(code)
+ 208 void test_subtract_mem_at_r32_from_r32_signed_overflow() {
+ 209   Reg[EAX].i = 0x2000;
+ 210   Reg[EBX].i = 0x80000000;  // smallest negative signed integer
+ 211   run(
+ 212       "== code 0x1\n"
+ 213       // op     ModR/M  SIB   displacement  immediate
+ 214       "  2b     18                                    \n"  // subtract *EAX from EBX
+ 215       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 216       "== data 0x2000\n"
+ 217       "ff ff ff 7f\n"  // largest positive signed integer
+ 218   );
+ 219   CHECK_TRACE_CONTENTS(
+ 220       "run: subtract r/m32 from EBX\n"
+ 221       "run: effective address is 0x00002000 (EAX)\n"
+ 222       "run: effective address contains 7fffffff\n"
+ 223       "run: SF=0; ZF=0; CF=0; OF=1\n"
+ 224       "run: storing 0x00000001\n"
+ 225   );
+ 226 }
+ 227 
+ 228 void test_subtract_mem_at_r32_from_r32_unsigned_overflow() {
+ 229   Reg[EAX].i = 0x2000;
+ 230   Reg[EBX].i = 0;
+ 231   run(
+ 232       "== code 0x1\n"
+ 233       // op     ModR/M  SIB   displacement  immediate
+ 234       "  2b     18                                    \n"  // subtract *EAX from EBX
+ 235       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 236       "== data 0x2000\n"
+ 237       "01 00 00 00\n"  // 1
+ 238   );
+ 239   CHECK_TRACE_CONTENTS(
+ 240       "run: subtract r/m32 from EBX\n"
+ 241       "run: effective address is 0x00002000 (EAX)\n"
+ 242       "run: effective address contains 1\n"
+ 243       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 244       "run: storing 0xffffffff\n"
+ 245   );
+ 246 }
+ 247 
+ 248 void test_subtract_mem_at_r32_from_r32_signed_and_unsigned_overflow() {
+ 249   Reg[EAX].i = 0x2000;
+ 250   Reg[EBX].i = 0;
+ 251   run(
+ 252       "== code 0x1\n"
+ 253       // op     ModR/M  SIB   displacement  immediate
+ 254       "  2b     18                                    \n"  // subtract *EAX from EBX
+ 255       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 256       "== data 0x2000\n"
+ 257       "00 00 00 80\n"  // smallest negative signed integer
+ 258   );
+ 259   CHECK_TRACE_CONTENTS(
+ 260       "run: subtract r/m32 from EBX\n"
+ 261       "run: effective address is 0x00002000 (EAX)\n"
+ 262       "run: effective address contains 80000000\n"
+ 263       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 264       "run: storing 0x80000000\n"
+ 265   );
+ 266 }
+ 267 
+ 268 //:: and
+ 269 :(code)
+ 270 void test_and_r32_with_mem_at_r32() {
+ 271   Reg[EAX].i = 0x2000;
+ 272   Reg[EBX].i = 0xff;
+ 273   run(
+ 274       "== code 0x1\n"
+ 275       // op     ModR/M  SIB   displacement  immediate
+ 276       "  21     18                                    \n"  // and EBX with *EAX
+ 277       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 278       "== data 0x2000\n"
+ 279       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 280   );
+ 281   CHECK_TRACE_CONTENTS(
+ 282       "run: and EBX with r/m32\n"
+ 283       "run: effective address is 0x00002000 (EAX)\n"
+ 284       "run: storing 0x0000000d\n"
+ 285   );
+ 286 }
+ 287 
+ 288 //:
+ 289 
+ 290 :(before "End Initialize Op Names")
+ 291 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)");
+ 292 
+ 293 :(code)
+ 294 void test_and_mem_at_r32_with_r32() {
+ 295   Reg[EAX].i = 0x2000;
+ 296   Reg[EBX].i = 0x0a0b0c0d;
+ 297   run(
+ 298       "== code 0x1\n"
+ 299       // op     ModR/M  SIB   displacement  immediate
+ 300       "  23     18                                    \n"  // and *EAX with EBX
+ 301       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 302       "== data 0x2000\n"
+ 303       "ff 00 00 00\n"  // 0x000000ff
+ 304   );
+ 305   CHECK_TRACE_CONTENTS(
+ 306       "run: and r/m32 with EBX\n"
+ 307       "run: effective address is 0x00002000 (EAX)\n"
+ 308       "run: storing 0x0000000d\n"
+ 309   );
+ 310 }
+ 311 
+ 312 :(before "End Single-Byte Opcodes")
+ 313 case 0x23: {  // and r/m32 with r32
+ 314   const uint8_t modrm = next();
+ 315   const uint8_t arg1 = (modrm>>3)&0x7;
+ 316   trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end();
+ 317   // bitwise ops technically operate on unsigned numbers, but it makes no
+ 318   // difference
+ 319   const int32_t* signed_arg2 = effective_address(modrm);
+ 320   Reg[arg1].i &= *signed_arg2;
+ 321   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
+ 322   SF = (Reg[arg1].i >> 31);
+ 323   ZF = (Reg[arg1].i == 0);
+ 324   CF = false;
+ 325   OF = false;
+ 326   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 327   break;
+ 328 }
+ 329 
+ 330 //:: or
+ 331 
+ 332 :(code)
+ 333 void test_or_r32_with_mem_at_r32() {
+ 334   Reg[EAX].i = 0x2000;
+ 335   Reg[EBX].i = 0xa0b0c0d0;
+ 336   run(
+ 337       "== code 0x1\n"
+ 338       // op     ModR/M  SIB   displacement  immediate
+ 339       "  09     18                                   #\n"  // EBX with *EAX
+ 340       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 341       "== data 0x2000\n"
+ 342       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 343   );
+ 344   CHECK_TRACE_CONTENTS(
+ 345       "run: or EBX with r/m32\n"
+ 346       "run: effective address is 0x00002000 (EAX)\n"
+ 347       "run: storing 0xaabbccdd\n"
+ 348   );
+ 349 }
+ 350 
+ 351 //:
+ 352 
+ 353 :(before "End Initialize Op Names")
+ 354 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
+ 355 
+ 356 :(code)
+ 357 void test_or_mem_at_r32_with_r32() {
+ 358   Reg[EAX].i = 0x2000;
+ 359   Reg[EBX].i = 0xa0b0c0d0;
+ 360   run(
+ 361       "== code 0x1\n"
+ 362       // op     ModR/M  SIB   displacement  immediate
+ 363       "  0b     18                                    \n"  // or *EAX with EBX
+ 364       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 365       "== data 0x2000\n"
+ 366       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 367   );
+ 368   CHECK_TRACE_CONTENTS(
+ 369       "run: or r/m32 with EBX\n"
+ 370       "run: effective address is 0x00002000 (EAX)\n"
+ 371       "run: storing 0xaabbccdd\n"
+ 372   );
+ 373 }
+ 374 
+ 375 :(before "End Single-Byte Opcodes")
+ 376 case 0x0b: {  // or r/m32 with r32
+ 377   const uint8_t modrm = next();
+ 378   const uint8_t arg1 = (modrm>>3)&0x7;
+ 379   trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end();
+ 380   // bitwise ops technically operate on unsigned numbers, but it makes no
+ 381   // difference
+ 382   const int32_t* signed_arg2 = effective_address(modrm);
+ 383   Reg[arg1].i |= *signed_arg2;
+ 384   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
+ 385   SF = (Reg[arg1].i >> 31);
+ 386   ZF = (Reg[arg1].i == 0);
+ 387   CF = false;
+ 388   OF = false;
+ 389   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 390   break;
+ 391 }
+ 392 
+ 393 //:: xor
+ 394 
+ 395 :(code)
+ 396 void test_xor_r32_with_mem_at_r32() {
+ 397   Reg[EAX].i = 0x2000;
+ 398   Reg[EBX].i = 0xa0b0c0d0;
+ 399   run(
+ 400       "== code 0x1\n"
+ 401       // op     ModR/M  SIB   displacement  immediate
+ 402       "  31     18                                    \n"  // xor EBX with *EAX
+ 403       "== data 0x2000\n"
+ 404       "0d 0c bb aa\n"  // 0xaabb0c0d
+ 405   );
+ 406   CHECK_TRACE_CONTENTS(
+ 407       "run: xor EBX with r/m32\n"
+ 408       "run: effective address is 0x00002000 (EAX)\n"
+ 409       "run: storing 0x0a0bccdd\n"
+ 410   );
+ 411 }
+ 412 
+ 413 //:
+ 414 
+ 415 :(before "End Initialize Op Names")
+ 416 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
+ 417 
+ 418 :(code)
+ 419 void test_xor_mem_at_r32_with_r32() {
+ 420   Reg[EAX].i = 0x2000;
+ 421   Reg[EBX].i = 0xa0b0c0d0;
+ 422   run(
+ 423       "== code 0x1\n"
+ 424       // op     ModR/M  SIB   displacement  immediate
+ 425       "  33     18                                    \n"  // xor *EAX with EBX
+ 426       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 427       "== data 0x2000\n"
+ 428       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 429   );
+ 430   CHECK_TRACE_CONTENTS(
+ 431       "run: xor r/m32 with EBX\n"
+ 432       "run: effective address is 0x00002000 (EAX)\n"
+ 433       "run: storing 0xaabbccdd\n"
+ 434   );
+ 435 }
+ 436 
+ 437 :(before "End Single-Byte Opcodes")
+ 438 case 0x33: {  // xor r/m32 with r32
+ 439   const uint8_t modrm = next();
+ 440   const uint8_t arg1 = (modrm>>3)&0x7;
+ 441   trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end();
+ 442   // bitwise ops technically operate on unsigned numbers, but it makes no
+ 443   // difference
+ 444   const int32_t* signed_arg2 = effective_address(modrm);
+ 445   Reg[arg1].i |= *signed_arg2;
+ 446   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
+ 447   SF = (Reg[arg1].i >> 31);
+ 448   ZF = (Reg[arg1].i == 0);
+ 449   CF = false;
+ 450   OF = false;
+ 451   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 452   break;
+ 453 }
+ 454 
+ 455 //:: not
+ 456 
+ 457 :(code)
+ 458 void test_not_of_mem_at_r32() {
+ 459   Reg[EBX].i = 0x2000;
+ 460   run(
+ 461       "== code 0x1\n"
+ 462       // op     ModR/M  SIB   displacement  immediate
+ 463       "  f7     13                                    \n"  // not *EBX
+ 464       // ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX)
+ 465       "== data 0x2000\n"
+ 466       "ff 00 0f 0f\n"  // 0x0f0f00ff
+ 467   );
+ 468   CHECK_TRACE_CONTENTS(
+ 469       "run: operate on r/m32\n"
+ 470       "run: effective address is 0x00002000 (EBX)\n"
+ 471       "run: subop: not\n"
+ 472       "run: storing 0xf0f0ff00\n"
+ 473   );
+ 474 }
+ 475 
+ 476 //:: compare (cmp)
+ 477 
+ 478 :(code)
+ 479 void test_compare_mem_at_r32_with_r32_greater() {
+ 480   Reg[EAX].i = 0x2000;
+ 481   Reg[EBX].i = 0x0a0b0c07;
+ 482   run(
+ 483       "== code 0x1\n"
+ 484       // op     ModR/M  SIB   displacement  immediate
+ 485       "  39     18                                    \n"  // compare *EAX with EBX
+ 486       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 487       "== data 0x2000\n"
+ 488       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 489   );
+ 490   CHECK_TRACE_CONTENTS(
+ 491       "run: compare r/m32 with EBX\n"
+ 492       "run: effective address is 0x00002000 (EAX)\n"
+ 493       "run: SF=0; ZF=0; CF=0; OF=0\n"
+ 494   );
+ 495 }
+ 496 
+ 497 :(code)
+ 498 void test_compare_mem_at_r32_with_r32_lesser() {
+ 499   Reg[EAX].i = 0x2000;
+ 500   Reg[EBX].i = 0x0a0b0c0d;
+ 501   run(
+ 502       "== code 0x1\n"
+ 503       // op     ModR/M  SIB   displacement  immediate
+ 504       "  39     18                                    \n"  // compare *EAX with EBX
+ 505       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 506       "== data 0x2000\n"
+ 507       "07 0c 0b 0a\n"  // 0x0a0b0c0d
+ 508   );
+ 509   CHECK_TRACE_CONTENTS(
+ 510       "run: compare r/m32 with EBX\n"
+ 511       "run: effective address is 0x00002000 (EAX)\n"
+ 512       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 513   );
+ 514 }
+ 515 
+ 516 :(code)
+ 517 void test_compare_mem_at_r32_with_r32_equal() {
+ 518   Reg[EAX].i = 0x2000;
+ 519   Reg[EBX].i = 0x0a0b0c0d;
+ 520   run(
+ 521       "== code 0x1\n"
+ 522       // op     ModR/M  SIB   displacement  immediate
+ 523       "  39     18                                    \n"  // compare *EAX and EBX
+ 524       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 525       "== data 0x2000\n"
+ 526       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 527   );
+ 528   CHECK_TRACE_CONTENTS(
+ 529       "run: compare r/m32 with EBX\n"
+ 530       "run: effective address is 0x00002000 (EAX)\n"
+ 531       "run: SF=0; ZF=1; CF=0; OF=0\n"
+ 532   );
+ 533 }
+ 534 
+ 535 //:
+ 536 
+ 537 :(before "End Initialize Op Names")
+ 538 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
+ 539 
+ 540 :(code)
+ 541 void test_compare_r32_with_mem_at_r32_greater() {
+ 542   Reg[EAX].i = 0x2000;
+ 543   Reg[EBX].i = 0x0a0b0c0d;
+ 544   run(
+ 545       "== code 0x1\n"
+ 546       // op     ModR/M  SIB   displacement  immediate
+ 547       "  3b     18                                    \n"  // compare EBX with *EAX
+ 548       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 549       "== data 0x2000\n"
+ 550       "07 0c 0b 0a\n"  // 0x0a0b0c07
+ 551   );
+ 552   CHECK_TRACE_CONTENTS(
+ 553       "run: compare EBX with r/m32\n"
+ 554       "run: effective address is 0x00002000 (EAX)\n"
+ 555       "run: SF=0; ZF=0; CF=0; OF=0\n"
+ 556   );
+ 557 }
+ 558 
+ 559 :(before "End Single-Byte Opcodes")
+ 560 case 0x3b: {  // set SF if r32 < r/m32
+ 561   const uint8_t modrm = next();
+ 562   const uint8_t reg1 = (modrm>>3)&0x7;
+ 563   trace(Callstack_depth+1, "run") << "compare " << rname(reg1) << " with r/m32" << end();
+ 564   const int32_t* signed_arg2 = effective_address(modrm);
+ 565   const int32_t signed_difference = Reg[reg1].i - *signed_arg2;
+ 566   SF = (signed_difference < 0);
+ 567   ZF = (signed_difference == 0);
+ 568   int64_t full_signed_difference = static_cast<int64_t>(Reg[reg1].i) - *signed_arg2;
+ 569   OF = (signed_difference != full_signed_difference);
+ 570   const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
+ 571   const uint32_t unsigned_difference = Reg[reg1].u - unsigned_arg2;
+ 572   const uint64_t full_unsigned_difference = static_cast<uint64_t>(Reg[reg1].u) - unsigned_arg2;
+ 573   CF = (unsigned_difference != full_unsigned_difference);
+ 574   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 575   break;
+ 576 }
+ 577 
+ 578 :(code)
+ 579 void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed() {
+ 580   Reg[EAX].i = 0x2000;
+ 581   Reg[EBX].i = 0x0a0b0c07;
+ 582   run(
+ 583       "== code 0x1\n"
+ 584       // op     ModR/M  SIB   displacement  immediate
+ 585       "  3b     18                                    \n"  // compare EBX with *EAX
+ 586       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 587       "== data 0x2000\n"
+ 588       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 589   );
+ 590   CHECK_TRACE_CONTENTS(
+ 591       "run: compare EBX with r/m32\n"
+ 592       "run: effective address is 0x00002000 (EAX)\n"
+ 593       "run: effective address contains a0b0c0d\n"
+ 594       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 595   );
+ 596 }
+ 597 
+ 598 void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed_due_to_overflow() {
+ 599   Reg[EAX].i = 0x2000;
+ 600   Reg[EBX].i = 0x7fffffff;  // largest positive signed integer
+ 601   run(
+ 602       "== code 0x1\n"
+ 603       // op     ModR/M  SIB   displacement  immediate
+ 604       "  3b     18                                    \n"  // compare EBX with *EAX
+ 605       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 606       "== data 0x2000\n"
+ 607       "00 00 00 80\n"  // smallest negative signed integer
+ 608   );
+ 609   CHECK_TRACE_CONTENTS(
+ 610       "run: compare EBX with r/m32\n"
+ 611       "run: effective address is 0x00002000 (EAX)\n"
+ 612       "run: effective address contains 80000000\n"
+ 613       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 614   );
+ 615 }
+ 616 
+ 617 void test_compare_r32_with_mem_at_r32_lesser_signed() {
+ 618   Reg[EAX].i = 0x2000;
+ 619   Reg[EBX].i = 0xffffffff;  // -1
+ 620   run(
+ 621       "== code 0x1\n"
+ 622       // op     ModR/M  SIB   displacement  immediate
+ 623       "  3b     18                                    \n"  // compare EBX with *EAX
+ 624       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 625       "== data 0x2000\n"
+ 626       "01 00 00 00\n"  // 1
+ 627   );
+ 628   CHECK_TRACE_CONTENTS(
+ 629       "run: compare EBX with r/m32\n"
+ 630       "run: effective address is 0x00002000 (EAX)\n"
+ 631       "run: effective address contains 1\n"
+ 632       "run: SF=1; ZF=0; CF=0; OF=0\n"
+ 633   );
+ 634 }
+ 635 
+ 636 void test_compare_r32_with_mem_at_r32_lesser_unsigned() {
+ 637   Reg[EAX].i = 0x2000;
+ 638   Reg[EBX].i = 0x00000001;  // 1
+ 639   run(
+ 640       "== code 0x1\n"
+ 641       // op     ModR/M  SIB   displacement  immediate
+ 642       "  3b     18                                    \n"  // compare EBX with *EAX
+ 643       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 644       "== data 0x2000\n"
+ 645       "ff ff ff ff\n"  // -1
+ 646   );
+ 647   CHECK_TRACE_CONTENTS(
+ 648       "run: compare EBX with r/m32\n"
+ 649       "run: effective address is 0x00002000 (EAX)\n"
+ 650       "run: effective address contains ffffffff\n"
+ 651       "run: SF=0; ZF=0; CF=1; OF=0\n"
+ 652   );
+ 653 }
+ 654 
+ 655 void test_compare_r32_with_mem_at_r32_equal() {
+ 656   Reg[EAX].i = 0x2000;
+ 657   Reg[EBX].i = 0x0a0b0c0d;
+ 658   run(
+ 659       "== code 0x1\n"
+ 660       // op     ModR/M  SIB   displacement  immediate
+ 661       "  3b     18                                    \n"  // compare EBX with *EAX
+ 662       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 663       "== data 0x2000\n"
+ 664       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 665   );
+ 666   CHECK_TRACE_CONTENTS(
+ 667       "run: compare EBX with r/m32\n"
+ 668       "run: effective address is 0x00002000 (EAX)\n"
+ 669       "run: SF=0; ZF=1; CF=0; OF=0\n"
+ 670   );
+ 671 }
+ 672 
+ 673 //:: copy (mov)
+ 674 
+ 675 void test_copy_r32_to_mem_at_r32() {
+ 676   Reg[EBX].i = 0xaf;
+ 677   Reg[EAX].i = 0x60;
+ 678   run(
+ 679       "== code 0x1\n"
+ 680       // op     ModR/M  SIB   displacement  immediate
+ 681       "  89     18                                    \n"  // copy EBX to *EAX
+ 682       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
+ 683   );
+ 684   CHECK_TRACE_CONTENTS(
+ 685       "run: copy EBX to r/m32\n"
+ 686       "run: effective address is 0x00000060 (EAX)\n"
+ 687       "run: storing 0x000000af\n"
+ 688   );
+ 689 }
+ 690 
+ 691 //:
+ 692 
+ 693 :(before "End Initialize Op Names")
+ 694 put_new(Name, "8b", "copy rm32 to r32 (mov)");
+ 695 
+ 696 :(code)
+ 697 void test_copy_mem_at_r32_to_r32() {
+ 698   Reg[EAX].i = 0x2000;
+ 699   run(
+ 700       "== code 0x1\n"
+ 701       // op     ModR/M  SIB   displacement  immediate
+ 702       "  8b     18                                    \n"  // copy *EAX to EBX
+ 703       "== data 0x2000\n"
+ 704       "af 00 00 00\n"  // 0x000000af
+ 705   );
+ 706   CHECK_TRACE_CONTENTS(
+ 707       "run: copy r/m32 to EBX\n"
+ 708       "run: effective address is 0x00002000 (EAX)\n"
+ 709       "run: storing 0x000000af\n"
+ 710   );
+ 711 }
+ 712 
+ 713 :(before "End Single-Byte Opcodes")
+ 714 case 0x8b: {  // copy r32 to r/m32
+ 715   const uint8_t modrm = next();
+ 716   const uint8_t rdest = (modrm>>3)&0x7;
+ 717   trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end();
+ 718   const int32_t* src = effective_address(modrm);
+ 719   Reg[rdest].i = *src;
+ 720   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end();
+ 721   break;
+ 722 }
+ 723 
+ 724 //:: jump
+ 725 
+ 726 :(code)
+ 727 void test_jump_mem_at_r32() {
+ 728   Reg[EAX].i = 0x2000;
+ 729   run(
+ 730       "== code 0x1\n"
+ 731       // op     ModR/M  SIB   displacement  immediate
+ 732       "  ff     20                                    \n"  // jump to *EAX
+ 733       // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX)
+ 734       "  b8                                 00 00 00 01\n"
+ 735       "  b8                                 00 00 00 02\n"
+ 736       "== data 0x2000\n"
+ 737       "08 00 00 00\n"  // 0x00000008
+ 738   );
+ 739   CHECK_TRACE_CONTENTS(
+ 740       "run: 0x00000001 opcode: ff\n"
+ 741       "run: jump to r/m32\n"
+ 742       "run: effective address is 0x00002000 (EAX)\n"
+ 743       "run: jumping to 0x00000008\n"
+ 744       "run: 0x00000008 opcode: b8\n"
+ 745   );
+ 746   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: b8");
+ 747 }
+ 748 
+ 749 :(before "End Op ff Subops")
+ 750 case 4: {  // jump to r/m32
+ 751   trace(Callstack_depth+1, "run") << "jump to r/m32" << end();
+ 752   const int32_t* arg2 = effective_address(modrm);
+ 753   EIP = *arg2;
+ 754   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
+ 755   break;
+ 756 }
+ 757 
+ 758 //:: push
+ 759 
+ 760 :(code)
+ 761 void test_push_mem_at_r32() {
+ 762   Reg[EAX].i = 0x2000;
+ 763   Mem.push_back(vma(0xbd000000));  // manually allocate memory
+ 764   Reg[ESP].u = 0xbd000014;
+ 765   run(
+ 766       "== code 0x1\n"
+ 767       // op     ModR/M  SIB   displacement  immediate
+ 768       "  ff     30                                    \n"  // push *EAX to stack
+ 769       "== data 0x2000\n"
+ 770       "af 00 00 00\n"  // 0x000000af
+ 771   );
+ 772   CHECK_TRACE_CONTENTS(
+ 773       "run: push r/m32\n"
+ 774       "run: effective address is 0x00002000 (EAX)\n"
+ 775       "run: decrementing ESP to 0xbd000010\n"
+ 776       "run: pushing value 0x000000af\n"
+ 777   );
+ 778 }
+ 779 
+ 780 :(before "End Op ff Subops")
+ 781 case 6: {  // push r/m32 to stack
+ 782   trace(Callstack_depth+1, "run") << "push r/m32" << end();
+ 783   const int32_t* val = effective_address(modrm);
+ 784   push(*val);
+ 785   break;
+ 786 }
+ 787 
+ 788 //:: pop
+ 789 
+ 790 :(before "End Initialize Op Names")
+ 791 put_new(Name, "8f", "pop top of stack to rm32 (pop)");
+ 792 
+ 793 :(code)
+ 794 void test_pop_mem_at_r32() {
+ 795   Reg[EAX].i = 0x60;
+ 796   Mem.push_back(vma(0xbd000000));  // manually allocate memory
+ 797   Reg[ESP].u = 0xbd000000;
+ 798   write_mem_i32(0xbd000000, 0x00000030);
+ 799   run(
+ 800       "== code 0x1\n"
+ 801       // op     ModR/M  SIB   displacement  immediate
+ 802       "  8f     00                                    \n"  // pop stack into *EAX
+ 803       // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX)
+ 804   );
+ 805   CHECK_TRACE_CONTENTS(
+ 806       "run: pop into r/m32\n"
+ 807       "run: effective address is 0x00000060 (EAX)\n"
+ 808       "run: popping value 0x00000030\n"
+ 809       "run: incrementing ESP to 0xbd000004\n"
+ 810   );
+ 811 }
+ 812 
+ 813 :(before "End Single-Byte Opcodes")
+ 814 case 0x8f: {  // pop stack into r/m32
+ 815   const uint8_t modrm = next();
+ 816   const uint8_t subop = (modrm>>3)&0x7;
+ 817   switch (subop) {
+ 818     case 0: {
+ 819       trace(Callstack_depth+1, "run") << "pop into r/m32" << end();
+ 820       int32_t* dest = effective_address(modrm);
+ 821       *dest = pop();
+ 822       break;
+ 823     }
+ 824   }
+ 825   break;
+ 826 }
+ 827 
+ 828 //:: special-case for loading address from disp32 rather than register
+ 829 
+ 830 :(code)
+ 831 void test_add_r32_to_mem_at_displacement() {
+ 832   Reg[EBX].i = 0x10;  // source
+ 833   run(
+ 834       "== code 0x1\n"
+ 835       // op     ModR/M  SIB   displacement  immediate
+ 836       "  01     1d            00 20 00 00             \n"  // add EBX to *0x2000
+ 837       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32)
+ 838       "== data 0x2000\n"
+ 839       "01 00 00 00\n"  // 0x00000001
+ 840   );
+ 841   CHECK_TRACE_CONTENTS(
+ 842       "run: add EBX to r/m32\n"
+ 843       "run: effective address is 0x00002000 (disp32)\n"
+ 844       "run: storing 0x00000011\n"
+ 845   );
+ 846 }
+ 847 
+ 848 :(before "End Mod 0 Special-cases(addr)")
+ 849 case 5:  // exception: mod 0b00 rm 0b101 => incoming disp32
+ 850   addr = next32();
+ 851   trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
+ 852   break;
+ 853 
+ 854 //:
+ 855 
+ 856 :(code)
+ 857 void test_add_r32_to_mem_at_r32_plus_disp8() {
+ 858   Reg[EBX].i = 0x10;  // source
+ 859   Reg[EAX].i = 0x1ffe;  // dest
+ 860   run(
+ 861       "== code 0x1\n"
+ 862       // op     ModR/M  SIB   displacement  immediate
+ 863       "  01     58            02                      \n"  // add EBX to *(EAX+2)
+ 864       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
+ 865       "== data 0x2000\n"
+ 866       "01 00 00 00\n"  // 0x00000001
+ 867   );
+ 868   CHECK_TRACE_CONTENTS(
+ 869       "run: add EBX to r/m32\n"
+ 870       "run: effective address is initially 0x00001ffe (EAX)\n"
+ 871       "run: effective address is 0x00002000 (after adding disp8)\n"
+ 872       "run: storing 0x00000011\n"
+ 873   );
+ 874 }
+ 875 
+ 876 :(before "End Mod Special-cases(addr)")
+ 877 case 1: {  // indirect + disp8 addressing
+ 878   switch (rm) {
+ 879   default:
+ 880     addr = Reg[rm].u;
+ 881     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
+ 882     break;
+ 883   // End Mod 1 Special-cases(addr)
+ 884   }
+ 885   int8_t displacement = static_cast<int8_t>(next());
+ 886   if (addr > 0) {
+ 887     addr += displacement;
+ 888     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
+ 889   }
+ 890   else {
+ 891     trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end();
+ 892   }
+ 893   break;
+ 894 }
+ 895 
+ 896 :(code)
+ 897 void test_add_r32_to_mem_at_r32_plus_negative_disp8() {
+ 898   Reg[EBX].i = 0x10;  // source
+ 899   Reg[EAX].i = 0x2001;  // dest
+ 900   run(
+ 901       "== code 0x1\n"
+ 902       // op     ModR/M  SIB   displacement  immediate
+ 903       "  01     58            ff                      \n"  // add EBX to *(EAX-1)
+ 904       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
+ 905       "== data 0x2000\n"
+ 906       "01 00 00 00\n"  // 0x00000001
+ 907   );
+ 908   CHECK_TRACE_CONTENTS(
+ 909       "run: add EBX to r/m32\n"
+ 910       "run: effective address is initially 0x00002001 (EAX)\n"
+ 911       "run: effective address is 0x00002000 (after adding disp8)\n"
+ 912       "run: storing 0x00000011\n"
+ 913   );
+ 914 }
+ 915 
+ 916 //:
+ 917 
+ 918 :(code)
+ 919 void test_add_r32_to_mem_at_r32_plus_disp32() {
+ 920   Reg[EBX].i = 0x10;  // source
+ 921   Reg[EAX].i = 0x1ffe;  // dest
+ 922   run(
+ 923       "== code 0x1\n"
+ 924       // op     ModR/M  SIB   displacement  immediate
+ 925       "  01     98            02 00 00 00             \n"  // add EBX to *(EAX+2)
+ 926       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
+ 927       "== data 0x2000\n"
+ 928       "01 00 00 00\n"  // 0x00000001
+ 929   );
+ 930   CHECK_TRACE_CONTENTS(
+ 931       "run: add EBX to r/m32\n"
+ 932       "run: effective address is initially 0x00001ffe (EAX)\n"
+ 933       "run: effective address is 0x00002000 (after adding disp32)\n"
+ 934       "run: storing 0x00000011\n"
+ 935   );
+ 936 }
+ 937 
+ 938 :(before "End Mod Special-cases(addr)")
+ 939 case 2:  // indirect + disp32 addressing
+ 940   switch (rm) {
+ 941   default:
+ 942     addr = Reg[rm].u;
+ 943     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
+ 944     break;
+ 945   // End Mod 2 Special-cases(addr)
+ 946   }
+ 947   if (addr > 0) {
+ 948     addr += next32();
+ 949     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
+ 950   }
+ 951   break;
+ 952 
+ 953 :(code)
+ 954 void test_add_r32_to_mem_at_r32_plus_negative_disp32() {
+ 955   Reg[EBX].i = 0x10;  // source
+ 956   Reg[EAX].i = 0x2001;  // dest
+ 957   run(
+ 958       "== code 0x1\n"
+ 959       // op     ModR/M  SIB   displacement  immediate
+ 960       "  01     98            ff ff ff ff             \n"  // add EBX to *(EAX-1)
+ 961       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
+ 962       "== data 0x2000\n"
+ 963       "01 00 00 00\n"  // 0x00000001
+ 964   );
+ 965   CHECK_TRACE_CONTENTS(
+ 966       "run: add EBX to r/m32\n"
+ 967       "run: effective address is initially 0x00002001 (EAX)\n"
+ 968       "run: effective address is 0x00002000 (after adding disp32)\n"
+ 969       "run: storing 0x00000011\n"
+ 970   );
+ 971 }
+ 972 
+ 973 //:: copy address (lea)
+ 974 
+ 975 :(before "End Initialize Op Names")
+ 976 put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
+ 977 
+ 978 :(code)
+ 979 void test_copy_address() {
+ 980   Reg[EAX].u = 0x2000;
+ 981   run(
+ 982       "== code 0x1\n"
+ 983       // op     ModR/M  SIB   displacement  immediate
+ 984       "  8d     18                                    \n"  // copy address in EAX into EBX
+ 985       // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
+ 986   );
+ 987   CHECK_TRACE_CONTENTS(
+ 988       "run: copy address into EBX\n"
+ 989       "run: effective address is 0x00002000 (EAX)\n"
+ 990   );
+ 991 }
+ 992 
+ 993 :(before "End Single-Byte Opcodes")
+ 994 case 0x8d: {  // copy address of m32 to r32
+ 995   const uint8_t modrm = next();
+ 996   const uint8_t arg1 = (modrm>>3)&0x7;
+ 997   trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end();
+ 998   Reg[arg1].u = effective_address_number(modrm);
+ 999   break;
+1000 }
 
diff --git a/html/subx/015immediate_addressing.cc.html b/html/subx/015immediate_addressing.cc.html index f260fb8c..087f04db 100644 --- a/html/subx/015immediate_addressing.cc.html +++ b/html/subx/015immediate_addressing.cc.html @@ -3,8 +3,8 @@ Mu - subx/015immediate_addressing.cc - - + + @@ -16,13 +16,13 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .CommentedCode { color: #8a8a8a; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .SalientComment { color: #0000af; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } .cSpecial { color: #008000; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -62,12 +62,12 @@ if ('onhashchange' in window) { 1 //: instructions that (immediately) contain an argument to act with 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "05", "add imm32 to EAX (add)"); + 4 put_new(Name, "05", "add imm32 to EAX (add)"); 5 6 :(before "End Single-Byte Opcodes") 7 case 0x05: { // add imm32 to EAX 8 int32_t signed_arg2 = next32(); - 9 trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to EAX" << end(); + 9 trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to EAX" << end(); 10 int32_t signed_result = Reg[EAX].i + signed_arg2; 11 SF = (signed_result < 0); 12 ZF = (signed_result == 0); @@ -80,7 +80,7 @@ if ('onhashchange' in window) { 19 CF = (unsigned_result != unsigned_full_result); 20 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 21 Reg[EAX].i = signed_result; - 22 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 22 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); 23 break; 24 } 25 @@ -131,7 +131,7 @@ if ('onhashchange' in window) { 70 //: 71 72 :(before "End Initialize Op Names") - 73 put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)"); + 73 put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)"); 74 75 :(code) 76 void test_add_imm32_to_r32() { @@ -154,10 +154,10 @@ if ('onhashchange' in window) { 93 :(before "End Single-Byte Opcodes") 94 case 0x81: { // combine r/m32 with imm32 95 trace(Callstack_depth+1, "run") << "combine r/m32 with imm32" << end(); - 96 const uint8_t modrm = next(); + 96 const uint8_t modrm = next(); 97 int32_t* signed_arg1 = effective_address(modrm); 98 const int32_t signed_arg2 = next32(); - 99 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << signed_arg2 << end(); + 99 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << signed_arg2 << end(); 100 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 101 switch (subop) { 102 case 0: { @@ -175,12 +175,12 @@ if ('onhashchange' in window) { 114 CF = (unsigned_result != unsigned_full_result); 115 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 116 *signed_arg1 = signed_result; - 117 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 117 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 118 break; 119 } 120 // End Op 81 Subops 121 default: - 122 cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n'; + 122 cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n'; 123 exit(1); 124 } 125 break; @@ -266,7 +266,7 @@ if ('onhashchange' in window) { 205 //:: subtract 206 207 :(before "End Initialize Op Names") - 208 put_new(Name, "2d", "subtract imm32 from EAX (sub)"); + 208 put_new(Name, "2d", "subtract imm32 from EAX (sub)"); 209 210 :(code) 211 void test_subtract_imm32_from_EAX() { @@ -285,7 +285,7 @@ if ('onhashchange' in window) { 224 :(before "End Single-Byte Opcodes") 225 case 0x2d: { // subtract imm32 from EAX 226 const int32_t signed_arg2 = next32(); - 227 trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << signed_arg2 << " from EAX" << end(); + 227 trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << signed_arg2 << " from EAX" << end(); 228 int32_t signed_result = Reg[EAX].i - signed_arg2; 229 SF = (signed_result < 0); 230 ZF = (signed_result == 0); @@ -298,7 +298,7 @@ if ('onhashchange' in window) { 237 CF = (unsigned_result != unsigned_full_result); 238 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 239 Reg[EAX].i = signed_result; - 240 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 240 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); 241 break; 242 } 243 @@ -382,7 +382,7 @@ if ('onhashchange' in window) { 321 CF = (unsigned_result != unsigned_full_result); 322 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 323 *signed_arg1 = signed_result; - 324 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 324 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 325 break; 326 } 327 @@ -472,7 +472,7 @@ if ('onhashchange' in window) { 411 //:: shift left 412 413 :(before "End Initialize Op Names") - 414 put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)"); + 414 put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)"); 415 416 :(code) 417 void test_shift_left_r32_with_imm8() { @@ -493,14 +493,14 @@ if ('onhashchange' in window) { 432 433 :(before "End Single-Byte Opcodes") 434 case 0xc1: { - 435 const uint8_t modrm = next(); + 435 const uint8_t modrm = next(); 436 trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); 437 int32_t* arg1 = effective_address(modrm); 438 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 439 switch (subop) { 440 case 4: { // shift left r/m32 by CL 441 trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end(); - 442 uint8_t count = next() & 0x1f; + 442 uint8_t count = next() & 0x1f; 443 // OF is only defined if count is 1 444 if (count == 1) { 445 bool msb = (*arg1 & 0x80000000) >> 1; @@ -512,12 +512,12 @@ if ('onhashchange' in window) { 451 SF = (*arg1 < 0); 452 // CF undefined 453 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 454 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 454 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 455 break; 456 } 457 // End Op c1 Subops 458 default: - 459 cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n'; + 459 cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n'; 460 exit(1); 461 } 462 break; @@ -545,7 +545,7 @@ if ('onhashchange' in window) { 484 :(before "End Op c1 Subops") 485 case 7: { // shift right r/m32 by CL, preserving sign 486 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end(); - 487 uint8_t count = next() & 0x1f; + 487 uint8_t count = next() & 0x1f; 488 int32_t result = (*arg1 >> count); 489 ZF = (*arg1 == 0); 490 SF = (*arg1 < 0); @@ -555,7 +555,7 @@ if ('onhashchange' in window) { 494 CF = ((*arg1 >> (count-1)) & 0x1); 495 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 496 *arg1 = result; - 497 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 497 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 498 break; 499 } 500 @@ -617,7 +617,7 @@ if ('onhashchange' in window) { 556 :(before "End Op c1 Subops") 557 case 5: { // shift right r/m32 by CL, preserving sign 558 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); - 559 uint8_t count = next() & 0x1f; + 559 uint8_t count = next() & 0x1f; 560 // OF is only defined if count is 1 561 if (count == 1) { 562 bool msb = (*arg1 & 0x80000000) >> 1; @@ -631,7 +631,7 @@ if ('onhashchange' in window) { 570 SF = false; 571 // CF undefined 572 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 574 break; 575 } 576 @@ -672,7 +672,7 @@ if ('onhashchange' in window) { 611 //:: and 612 613 :(before "End Initialize Op Names") - 614 put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)"); + 614 put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)"); 615 616 :(code) 617 void test_and_EAX_with_imm32() { @@ -693,9 +693,9 @@ if ('onhashchange' in window) { 632 // bitwise ops technically operate on unsigned numbers, but it makes no 633 // difference 634 const int32_t signed_arg2 = next32(); - 635 trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + 635 trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); 636 Reg[EAX].i &= signed_arg2; - 637 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 637 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); 638 SF = (Reg[EAX].i >> 31); 639 ZF = (Reg[EAX].i == 0); 640 CF = false; @@ -732,7 +732,7 @@ if ('onhashchange' in window) { 671 // bitwise ops technically operate on unsigned numbers, but it makes no 672 // difference 673 *signed_arg1 &= signed_arg2; - 674 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 674 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 675 SF = (*signed_arg1 >> 31); 676 ZF = (*signed_arg1 == 0); 677 CF = false; @@ -764,7 +764,7 @@ if ('onhashchange' in window) { 703 //:: or 704 705 :(before "End Initialize Op Names") - 706 put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)"); + 706 put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)"); 707 708 :(code) 709 void test_or_EAX_with_imm32() { @@ -785,9 +785,9 @@ if ('onhashchange' in window) { 724 // bitwise ops technically operate on unsigned numbers, but it makes no 725 // difference 726 const int32_t signed_arg2 = next32(); - 727 trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + 727 trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); 728 Reg[EAX].i |= signed_arg2; - 729 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 729 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); 730 SF = (Reg[EAX].i >> 31); 731 ZF = (Reg[EAX].i == 0); 732 CF = false; @@ -824,7 +824,7 @@ if ('onhashchange' in window) { 763 // bitwise ops technically operate on unsigned numbers, but it makes no 764 // difference 765 *signed_arg1 |= signed_arg2; - 766 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 766 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 767 SF = (*signed_arg1 >> 31); 768 ZF = (*signed_arg1 == 0); 769 CF = false; @@ -854,7 +854,7 @@ if ('onhashchange' in window) { 793 //:: xor 794 795 :(before "End Initialize Op Names") - 796 put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)"); + 796 put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)"); 797 798 :(code) 799 void test_xor_EAX_with_imm32() { @@ -875,9 +875,9 @@ if ('onhashchange' in window) { 814 // bitwise ops technically operate on unsigned numbers, but it makes no 815 // difference 816 const int32_t signed_arg2 = next32(); - 817 trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + 817 trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); 818 Reg[EAX].i ^= signed_arg2; - 819 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 819 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); 820 SF = (Reg[EAX].i >> 31); 821 ZF = (Reg[EAX].i == 0); 822 CF = false; @@ -914,7 +914,7 @@ if ('onhashchange' in window) { 853 // bitwise ops technically operate on unsigned numbers, but it makes no 854 // difference 855 *signed_arg1 ^= signed_arg2; - 856 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 856 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 857 SF = (*signed_arg1 >> 31); 858 ZF = (*signed_arg1 == 0); 859 CF = false; @@ -944,7 +944,7 @@ if ('onhashchange' in window) { 883 //:: compare (cmp) 884 885 :(before "End Initialize Op Names") - 886 put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)"); + 886 put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)"); 887 888 :(code) 889 void test_compare_EAX_with_imm32_greater() { @@ -964,7 +964,7 @@ if ('onhashchange' in window) { 903 case 0x3d: { // compare EAX with imm32 904 const int32_t signed_arg1 = Reg[EAX].i; 905 const int32_t signed_arg2 = next32(); - 906 trace(Callstack_depth+1, "run") << "compare EAX with imm32 0x" << HEXWORD << signed_arg2 << end(); + 906 trace(Callstack_depth+1, "run") << "compare EAX with imm32 0x" << HEXWORD << signed_arg2 << end(); 907 const int32_t signed_difference = signed_arg1 - signed_arg2; 908 SF = (signed_difference < 0); 909 ZF = (signed_difference == 0); @@ -1228,13 +1228,13 @@ if ('onhashchange' in window) { 1167 1168 :(before "End Initialize Op Names") 1169 // b8 defined earlier to copy imm32 to EAX -1170 put_new(Name, "b9", "copy imm32 to ECX (mov)"); -1171 put_new(Name, "ba", "copy imm32 to EDX (mov)"); -1172 put_new(Name, "bb", "copy imm32 to EBX (mov)"); -1173 put_new(Name, "bc", "copy imm32 to ESP (mov)"); -1174 put_new(Name, "bd", "copy imm32 to EBP (mov)"); -1175 put_new(Name, "be", "copy imm32 to ESI (mov)"); -1176 put_new(Name, "bf", "copy imm32 to EDI (mov)"); +1170 put_new(Name, "b9", "copy imm32 to ECX (mov)"); +1171 put_new(Name, "ba", "copy imm32 to EDX (mov)"); +1172 put_new(Name, "bb", "copy imm32 to EBX (mov)"); +1173 put_new(Name, "bc", "copy imm32 to ESP (mov)"); +1174 put_new(Name, "bd", "copy imm32 to EBP (mov)"); +1175 put_new(Name, "be", "copy imm32 to ESI (mov)"); +1176 put_new(Name, "bf", "copy imm32 to EDI (mov)"); 1177 1178 :(code) 1179 void test_copy_imm32_to_r32() { @@ -1258,7 +1258,7 @@ if ('onhashchange' in window) { 1197 case 0xbf: { // copy imm32 to r32 1198 const uint8_t rdest = op & 0x7; 1199 const int32_t src = next32(); -1200 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end(); +1200 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end(); 1201 Reg[rdest].i = src; 1202 break; 1203 } @@ -1266,7 +1266,7 @@ if ('onhashchange' in window) { 1205 //: 1206 1207 :(before "End Initialize Op Names") -1208 put_new(Name, "c7", "copy imm32 to rm32 (mov)"); +1208 put_new(Name, "c7", "copy imm32 to rm32 (mov)"); 1209 1210 :(code) 1211 void test_copy_imm32_to_mem_at_r32() { @@ -1286,16 +1286,16 @@ if ('onhashchange' in window) { 1225 1226 :(before "End Single-Byte Opcodes") 1227 case 0xc7: { // copy imm32 to r32 -1228 const uint8_t modrm = next(); +1228 const uint8_t modrm = next(); 1229 trace(Callstack_depth+1, "run") << "copy imm32 to r/m32" << end(); 1230 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 1231 if (subop != 0) { -1232 cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n"; +1232 cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n"; 1233 exit(1); 1234 } 1235 int32_t* dest = effective_address(modrm); 1236 const int32_t src = next32(); -1237 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end(); +1237 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end(); 1238 *dest = src; 1239 break; 1240 } @@ -1303,7 +1303,7 @@ if ('onhashchange' in window) { 1242 //:: push 1243 1244 :(before "End Initialize Op Names") -1245 put_new(Name, "68", "push imm32 to stack (push)"); +1245 put_new(Name, "68", "push imm32 to stack (push)"); 1246 1247 :(code) 1248 void test_push_imm32() { @@ -1324,11 +1324,11 @@ if ('onhashchange' in window) { 1263 :(before "End Single-Byte Opcodes") 1264 case 0x68: { 1265 const uint32_t val = static_cast<uint32_t>(next32()); -1266 trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end(); +1266 trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end(); 1267 //? cerr << "push: " << val << " => " << Reg[ESP].u << '\n'; 1268 push(val); -1269 trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end(); -1270 trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end(); +1269 trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end(); +1270 trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end(); 1271 break; 1272 } diff --git a/html/subx/016index_addressing.cc.html b/html/subx/016index_addressing.cc.html index 846f72e4..68841882 100644 --- a/html/subx/016index_addressing.cc.html +++ b/html/subx/016index_addressing.cc.html @@ -3,8 +3,8 @@ Mu - subx/016index_addressing.cc - - + + @@ -16,11 +16,11 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } .Constant { color: #008787; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } --> @@ -37,7 +37,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -85,27 +85,27 @@ if ('onhashchange' in window) { 27 break; 28 :(code) 29 uint32_t effective_address_from_sib(uint8_t mod) { - 30 const uint8_t sib = next(); + 30 const uint8_t sib = next(); 31 const uint8_t base = sib&0x7; 32 uint32_t addr = 0; 33 if (base != EBP || mod != 0) { 34 addr = Reg[base].u; - 35 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end(); + 35 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end(); 36 } 37 else { 38 // base == EBP && mod == 0 39 addr = next32(); // ignore base - 40 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end(); + 40 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end(); 41 } 42 const uint8_t index = (sib>>3)&0x7; 43 if (index == ESP) { 44 // ignore index and scale - 45 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end(); + 45 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end(); 46 } 47 else { 48 const uint8_t scale = (1 << (sib>>6)); 49 addr += Reg[index].i*scale; // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative. - 50 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end(); + 50 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end(); 51 } 52 return addr; 53 } diff --git a/html/subx/017jump_disp8.cc.html b/html/subx/017jump_disp8.cc.html index 51cd9e97..1516a8ff 100644 --- a/html/subx/017jump_disp8.cc.html +++ b/html/subx/017jump_disp8.cc.html @@ -3,8 +3,8 @@ Mu - subx/017jump_disp8.cc - - + + @@ -15,12 +15,12 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } .SalientComment { color: #0000af; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -62,10 +62,10 @@ if ('onhashchange' in window) { 3 //:: jump 4 5 :(before "End Initialize Op Names") - 6 put_new(Name, "eb", "jump disp8 bytes away (jmp)"); + 6 put_new(Name, "eb", "jump disp8 bytes away (jmp)"); 7 8 :(code) - 9 void test_jump_rel8() { + 9 void test_jump_disp8() { 10 run( 11 "== code 0x1\n" 12 // op ModR/M SIB displacement immediate @@ -82,9 +82,9 @@ if ('onhashchange' in window) { 23 } 24 25 :(before "End Single-Byte Opcodes") - 26 case 0xeb: { // jump rel8 - 27 int8_t offset = static_cast<int>(next()); - 28 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + 26 case 0xeb: { // jump disp8 + 27 int8_t offset = static_cast<int>(next()); + 28 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 29 EIP += offset; 30 break; 31 } @@ -92,10 +92,10 @@ if ('onhashchange' in window) { 33 //:: jump if equal/zero 34 35 :(before "End Initialize Op Names") - 36 put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)"); + 36 put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)"); 37 38 :(code) - 39 void test_je_rel8_success() { + 39 void test_je_disp8_success() { 40 ZF = true; 41 run( 42 "== code 0x1\n" @@ -113,17 +113,17 @@ if ('onhashchange' in window) { 54 } 55 56 :(before "End Single-Byte Opcodes") - 57 case 0x74: { // jump rel8 if ZF - 58 const int8_t offset = static_cast<int>(next()); + 57 case 0x74: { // jump disp8 if ZF + 58 const int8_t offset = static_cast<int>(next()); 59 if (ZF) { - 60 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + 60 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 61 EIP += offset; 62 } 63 break; 64 } 65 66 :(code) - 67 void test_je_rel8_fail() { + 67 void test_je_disp8_fail() { 68 ZF = false; 69 run( 70 "== code 0x1\n" @@ -143,10 +143,10 @@ if ('onhashchange' in window) { 84 //:: jump if not equal/not zero 85 86 :(before "End Initialize Op Names") - 87 put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); + 87 put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); 88 89 :(code) - 90 void test_jne_rel8_success() { + 90 void test_jne_disp8_success() { 91 ZF = false; 92 run( 93 "== code 0x1\n" @@ -164,17 +164,17 @@ if ('onhashchange' in window) { 105 } 106 107 :(before "End Single-Byte Opcodes") -108 case 0x75: { // jump rel8 unless ZF -109 const int8_t offset = static_cast<int>(next()); +108 case 0x75: { // jump disp8 unless ZF +109 const int8_t offset = static_cast<int>(next()); 110 if (!ZF) { -111 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +111 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 112 EIP += offset; 113 } 114 break; 115 } 116 117 :(code) -118 void test_jne_rel8_fail() { +118 void test_jne_disp8_fail() { 119 ZF = true; 120 run( 121 "== code 0x1\n" @@ -194,11 +194,11 @@ if ('onhashchange' in window) { 135 //:: jump if greater 136 137 :(before "End Initialize Op Names") -138 put_new(Name, "7f", "jump disp8 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); -139 put_new(Name, "77", "jump disp8 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); +138 put_new(Name, "7f", "jump disp8 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); +139 put_new(Name, "77", "jump disp8 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); 140 141 :(code) -142 void test_jg_rel8_success() { +142 void test_jg_disp8_success() { 143 ZF = false; 144 SF = false; 145 OF = false; @@ -218,25 +218,25 @@ if ('onhashchange' in window) { 159 } 160 161 :(before "End Single-Byte Opcodes") -162 case 0x7f: { // jump rel8 if SF == OF and !ZF -163 const int8_t offset = static_cast<int>(next()); +162 case 0x7f: { // jump disp8 if SF == OF and !ZF +163 const int8_t offset = static_cast<int>(next()); 164 if (SF == OF && !ZF) { -165 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +165 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 166 EIP += offset; 167 } 168 break; 169 } -170 case 0x77: { // jump rel8 if !CF and !ZF -171 const int8_t offset = static_cast<int>(next()); +170 case 0x77: { // jump disp8 if !CF and !ZF +171 const int8_t offset = static_cast<int>(next()); 172 if (!CF && !ZF) { -173 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +173 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 174 EIP += offset; 175 } 176 break; 177 } 178 179 :(code) -180 void test_jg_rel8_fail() { +180 void test_jg_disp8_fail() { 181 ZF = false; 182 SF = true; 183 OF = false; @@ -258,11 +258,11 @@ if ('onhashchange' in window) { 199 //:: jump if greater or equal 200 201 :(before "End Initialize Op Names") -202 put_new(Name, "7d", "jump disp8 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); -203 put_new(Name, "73", "jump disp8 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); +202 put_new(Name, "7d", "jump disp8 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); +203 put_new(Name, "73", "jump disp8 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); 204 205 :(code) -206 void test_jge_rel8_success() { +206 void test_jge_disp8_success() { 207 SF = false; 208 OF = false; 209 run( @@ -281,25 +281,25 @@ if ('onhashchange' in window) { 222 } 223 224 :(before "End Single-Byte Opcodes") -225 case 0x7d: { // jump rel8 if SF == OF -226 const int8_t offset = static_cast<int>(next()); +225 case 0x7d: { // jump disp8 if SF == OF +226 const int8_t offset = static_cast<int>(next()); 227 if (SF == OF) { -228 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +228 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 229 EIP += offset; 230 } 231 break; 232 } -233 case 0x73: { // jump rel8 if !CF -234 const int8_t offset = static_cast<int>(next()); +233 case 0x73: { // jump disp8 if !CF +234 const int8_t offset = static_cast<int>(next()); 235 if (!CF) { -236 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +236 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 237 EIP += offset; 238 } 239 break; 240 } 241 242 :(code) -243 void test_jge_rel8_fail() { +243 void test_jge_disp8_fail() { 244 SF = true; 245 OF = false; 246 run( @@ -320,11 +320,11 @@ if ('onhashchange' in window) { 261 //:: jump if lesser 262 263 :(before "End Initialize Op Names") -264 put_new(Name, "7c", "jump disp8 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); -265 put_new(Name, "72", "jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); +264 put_new(Name, "7c", "jump disp8 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); +265 put_new(Name, "72", "jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); 266 267 :(code) -268 void test_jl_rel8_success() { +268 void test_jl_disp8_success() { 269 ZF = false; 270 SF = true; 271 OF = false; @@ -344,25 +344,25 @@ if ('onhashchange' in window) { 285 } 286 287 :(before "End Single-Byte Opcodes") -288 case 0x7c: { // jump rel8 if SF != OF -289 const int8_t offset = static_cast<int>(next()); +288 case 0x7c: { // jump disp8 if SF != OF +289 const int8_t offset = static_cast<int>(next()); 290 if (SF != OF) { -291 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +291 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 292 EIP += offset; 293 } 294 break; 295 } -296 case 0x72: { // jump rel8 if CF -297 const int8_t offset = static_cast<int>(next()); +296 case 0x72: { // jump disp8 if CF +297 const int8_t offset = static_cast<int>(next()); 298 if (CF) { -299 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +299 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 300 EIP += offset; 301 } 302 break; 303 } 304 305 :(code) -306 void test_jl_rel8_fail() { +306 void test_jl_disp8_fail() { 307 ZF = false; 308 SF = false; 309 OF = false; @@ -384,11 +384,11 @@ if ('onhashchange' in window) { 325 //:: jump if lesser or equal 326 327 :(before "End Initialize Op Names") -328 put_new(Name, "7e", "jump disp8 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); -329 put_new(Name, "76", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); +328 put_new(Name, "7e", "jump disp8 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); +329 put_new(Name, "76", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); 330 331 :(code) -332 void test_jle_rel8_equal() { +332 void test_jle_disp8_equal() { 333 ZF = true; 334 SF = false; 335 OF = false; @@ -408,7 +408,7 @@ if ('onhashchange' in window) { 349 } 350 351 :(code) -352 void test_jle_rel8_lesser() { +352 void test_jle_disp8_lesser() { 353 ZF = false; 354 SF = true; 355 OF = false; @@ -428,25 +428,25 @@ if ('onhashchange' in window) { 369 } 370 371 :(before "End Single-Byte Opcodes") -372 case 0x7e: { // jump rel8 if ZF or SF != OF -373 const int8_t offset = static_cast<int>(next()); +372 case 0x7e: { // jump disp8 if ZF or SF != OF +373 const int8_t offset = static_cast<int>(next()); 374 if (ZF || SF != OF) { -375 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +375 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 376 EIP += offset; 377 } 378 break; 379 } -380 case 0x76: { // jump rel8 if ZF or CF -381 const int8_t offset = static_cast<int>(next()); +380 case 0x76: { // jump disp8 if ZF or CF +381 const int8_t offset = static_cast<int>(next()); 382 if (ZF || CF) { -383 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +383 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); 384 EIP += offset; 385 } 386 break; 387 } 388 389 :(code) -390 void test_jle_rel8_greater() { +390 void test_jle_disp8_greater() { 391 ZF = false; 392 SF = false; 393 OF = false; diff --git a/html/subx/018jump_disp32.cc.html b/html/subx/018jump_disp32.cc.html index 971dfea0..d6650806 100644 --- a/html/subx/018jump_disp32.cc.html +++ b/html/subx/018jump_disp32.cc.html @@ -3,8 +3,8 @@ Mu - subx/018jump_disp32.cc - - + + @@ -15,12 +15,12 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } .SalientComment { color: #0000af; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -62,7 +62,7 @@ if ('onhashchange' in window) { 3 //:: jump 4 5 :(before "End Initialize Op Names") - 6 put_new(Name, "e9", "jump disp32 bytes away (jmp)"); + 6 put_new(Name, "e9", "jump disp32 bytes away (jmp)"); 7 8 :(code) 9 void test_jump_disp32() { @@ -92,7 +92,7 @@ if ('onhashchange' in window) { 33 //:: jump if equal/zero 34 35 :(before "End Initialize Op Names") - 36 put_new(Name_0f, "84", "jump disp32 bytes away if equal, if ZF is set (jcc/jz/je)"); + 36 put_new(Name_0f, "84", "jump disp32 bytes away if equal, if ZF is set (jcc/jz/je)"); 37 38 :(code) 39 void test_je_disp32_success() { @@ -116,7 +116,7 @@ if ('onhashchange' in window) { 57 case 0x84: { // jump disp32 if ZF 58 const int32_t offset = next32(); 59 if (ZF) { - 60 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + 60 trace(Callstack_depth+1, "run") << "jump " << offset << end(); 61 EIP += offset; 62 } 63 break; @@ -143,7 +143,7 @@ if ('onhashchange' in window) { 84 //:: jump if not equal/not zero 85 86 :(before "End Initialize Op Names") - 87 put_new(Name_0f, "85", "jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); + 87 put_new(Name_0f, "85", "jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); 88 89 :(code) 90 void test_jne_disp32_success() { @@ -167,7 +167,7 @@ if ('onhashchange' in window) { 108 case 0x85: { // jump disp32 unless ZF 109 const int32_t offset = next32(); 110 if (!ZF) { -111 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +111 trace(Callstack_depth+1, "run") << "jump " << offset << end(); 112 EIP += offset; 113 } 114 break; @@ -194,240 +194,276 @@ if ('onhashchange' in window) { 135 //:: jump if greater 136 137 :(before "End Initialize Op Names") -138 put_new(Name_0f, "8f", "jump disp32 bytes away if greater, if ZF is unset and SF == OF (jcc/jg/jnle)"); -139 -140 :(code) -141 void test_jg_disp32_success() { -142 ZF = false; -143 SF = false; -144 OF = false; -145 run( -146 "== code 0x1\n" -147 // op ModR/M SIB displacement immediate -148 " 0f 8f 05 00 00 00 \n" // skip 1 instruction -149 " 05 00 00 00 01 \n" -150 " 05 00 00 00 02 \n" -151 ); -152 CHECK_TRACE_CONTENTS( -153 "run: 0x00000001 opcode: 0f\n" -154 "run: jump 5\n" -155 "run: 0x0000000c opcode: 05\n" -156 ); -157 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -158 } -159 -160 :(before "End Two-Byte Opcodes Starting With 0f") -161 case 0x8f: { // jump disp32 if !SF and !ZF -162 const int32_t offset = next32(); -163 if (!ZF && SF == OF) { -164 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -165 EIP += offset; -166 } -167 break; -168 } -169 -170 :(code) -171 void test_jg_disp32_fail() { -172 ZF = false; -173 SF = true; -174 OF = false; -175 run( -176 "== code 0x1\n" -177 // op ModR/M SIB displacement immediate -178 " 0f 8f 05 00 00 00 \n" // skip 1 instruction -179 " 05 00 00 00 01 \n" -180 " 05 00 00 00 02 \n" -181 ); -182 CHECK_TRACE_CONTENTS( -183 "run: 0x00000001 opcode: 0f\n" -184 "run: 0x00000007 opcode: 05\n" -185 "run: 0x0000000c opcode: 05\n" -186 ); -187 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -188 } -189 -190 //:: jump if greater or equal -191 -192 :(before "End Initialize Op Names") -193 put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal, if SF == OF (jcc/jge/jnl)"); -194 -195 :(code) -196 void test_jge_disp32_success() { -197 SF = false; -198 OF = false; -199 run( -200 "== code 0x1\n" -201 // op ModR/M SIB displacement immediate -202 " 0f 8d 05 00 00 00 \n" // skip 1 instruction -203 " 05 00 00 00 01 \n" -204 " 05 00 00 00 02 \n" -205 ); -206 CHECK_TRACE_CONTENTS( -207 "run: 0x00000001 opcode: 0f\n" -208 "run: jump 5\n" -209 "run: 0x0000000c opcode: 05\n" -210 ); -211 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -212 } -213 -214 :(before "End Two-Byte Opcodes Starting With 0f") -215 case 0x8d: { // jump disp32 if !SF -216 const int32_t offset = next32(); -217 if (SF == OF) { -218 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -219 EIP += offset; -220 } -221 break; +138 put_new(Name_0f, "8f", "jump disp32 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); +139 put_new(Name_0f, "87", "jump disp32 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); +140 +141 :(code) +142 void test_jg_disp32_success() { +143 ZF = false; +144 SF = false; +145 OF = false; +146 run( +147 "== code 0x1\n" +148 // op ModR/M SIB displacement immediate +149 " 0f 8f 05 00 00 00 \n" // skip 1 instruction +150 " 05 00 00 00 01 \n" +151 " 05 00 00 00 02 \n" +152 ); +153 CHECK_TRACE_CONTENTS( +154 "run: 0x00000001 opcode: 0f\n" +155 "run: jump 5\n" +156 "run: 0x0000000c opcode: 05\n" +157 ); +158 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +159 } +160 +161 :(before "End Two-Byte Opcodes Starting With 0f") +162 case 0x8f: { // jump disp32 if !SF and !ZF +163 const int32_t offset = next32(); +164 if (!ZF && SF == OF) { +165 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +166 EIP += offset; +167 } +168 break; +169 } +170 case 0x87: { // jump disp32 if !CF and !ZF +171 const int32_t offset = next(); +172 if (!CF && !ZF) { +173 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +174 EIP += offset; +175 } +176 break; +177 } +178 +179 :(code) +180 void test_jg_disp32_fail() { +181 ZF = false; +182 SF = true; +183 OF = false; +184 run( +185 "== code 0x1\n" +186 // op ModR/M SIB displacement immediate +187 " 0f 8f 05 00 00 00 \n" // skip 1 instruction +188 " 05 00 00 00 01 \n" +189 " 05 00 00 00 02 \n" +190 ); +191 CHECK_TRACE_CONTENTS( +192 "run: 0x00000001 opcode: 0f\n" +193 "run: 0x00000007 opcode: 05\n" +194 "run: 0x0000000c opcode: 05\n" +195 ); +196 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +197 } +198 +199 //:: jump if greater or equal +200 +201 :(before "End Initialize Op Names") +202 put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); +203 put_new(Name_0f, "83", "jump disp32 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); +204 +205 :(code) +206 void test_jge_disp32_success() { +207 SF = false; +208 OF = false; +209 run( +210 "== code 0x1\n" +211 // op ModR/M SIB displacement immediate +212 " 0f 8d 05 00 00 00 \n" // skip 1 instruction +213 " 05 00 00 00 01 \n" +214 " 05 00 00 00 02 \n" +215 ); +216 CHECK_TRACE_CONTENTS( +217 "run: 0x00000001 opcode: 0f\n" +218 "run: jump 5\n" +219 "run: 0x0000000c opcode: 05\n" +220 ); +221 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); 222 } 223 -224 :(code) -225 void test_jge_disp32_fail() { -226 SF = true; -227 OF = false; -228 run( -229 "== code 0x1\n" -230 // op ModR/M SIB displacement immediate -231 " 0f 8d 05 00 00 00 \n" // skip 1 instruction -232 " 05 00 00 00 01 \n" -233 " 05 00 00 00 02 \n" -234 ); -235 CHECK_TRACE_CONTENTS( -236 "run: 0x00000001 opcode: 0f\n" -237 "run: 0x00000007 opcode: 05\n" -238 "run: 0x0000000c opcode: 05\n" -239 ); -240 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -241 } -242 -243 //:: jump if lesser -244 -245 :(before "End Initialize Op Names") -246 put_new(Name_0f, "8c", "jump disp32 bytes away if lesser, if SF != OF (jcc/jl/jnge)"); -247 -248 :(code) -249 void test_jl_disp32_success() { -250 ZF = false; -251 SF = true; -252 OF = false; -253 run( -254 "== code 0x1\n" -255 // op ModR/M SIB displacement immediate -256 " 0f 8c 05 00 00 00 \n" // skip 1 instruction -257 " 05 00 00 00 01 \n" -258 " 05 00 00 00 02 \n" -259 ); -260 CHECK_TRACE_CONTENTS( -261 "run: 0x00000001 opcode: 0f\n" -262 "run: jump 5\n" -263 "run: 0x0000000c opcode: 05\n" -264 ); -265 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -266 } -267 -268 :(before "End Two-Byte Opcodes Starting With 0f") -269 case 0x8c: { // jump disp32 if SF and !ZF -270 const int32_t offset = next32(); -271 if (SF != OF) { -272 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -273 EIP += offset; -274 } -275 break; -276 } -277 -278 :(code) -279 void test_jl_disp32_fail() { -280 ZF = false; -281 SF = false; -282 OF = false; -283 run( -284 "== code 0x1\n" -285 // op ModR/M SIB displacement immediate -286 " 0f 8c 05 00 00 00 \n" // skip 1 instruction -287 " 05 00 00 00 01 \n" -288 " 05 00 00 00 02 \n" -289 ); -290 CHECK_TRACE_CONTENTS( -291 "run: 0x00000001 opcode: 0f\n" -292 "run: 0x00000007 opcode: 05\n" -293 "run: 0x0000000c opcode: 05\n" -294 ); -295 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -296 } -297 -298 //:: jump if lesser or equal -299 -300 :(before "End Initialize Op Names") -301 put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal, if ZF is set or SF != OF (jcc/jle/jng)"); -302 -303 :(code) -304 void test_jle_disp32_equal() { -305 ZF = true; -306 SF = false; -307 OF = false; -308 run( -309 "== code 0x1\n" -310 // op ModR/M SIB displacement immediate -311 " 0f 8e 05 00 00 00 \n" // skip 1 instruction -312 " 05 00 00 00 01 \n" -313 " 05 00 00 00 02 \n" -314 ); -315 CHECK_TRACE_CONTENTS( -316 "run: 0x00000001 opcode: 0f\n" -317 "run: jump 5\n" -318 "run: 0x0000000c opcode: 05\n" -319 ); -320 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -321 } -322 -323 :(code) -324 void test_jle_disp32_lesser() { -325 ZF = false; -326 SF = true; -327 OF = false; -328 run( -329 "== code 0x1\n" -330 // op ModR/M SIB displacement immediate -331 " 0f 8e 05 00 00 00 \n" // skip 1 instruction -332 " 05 00 00 00 01 \n" -333 " 05 00 00 00 02 \n" -334 ); -335 CHECK_TRACE_CONTENTS( -336 "run: 0x00000001 opcode: 0f\n" -337 "run: jump 5\n" -338 "run: 0x0000000c opcode: 05\n" -339 ); -340 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -341 } -342 -343 :(before "End Two-Byte Opcodes Starting With 0f") -344 case 0x8e: { // jump disp32 if SF or ZF -345 const int32_t offset = next32(); -346 if (ZF || SF != OF) { -347 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -348 EIP += offset; -349 } -350 break; -351 } -352 -353 :(code) -354 void test_jle_disp32_greater() { -355 ZF = false; -356 SF = false; -357 OF = false; -358 run( -359 "== code 0x1\n" -360 // op ModR/M SIB displacement immediate -361 " 0f 8e 05 00 00 00 \n" // skip 1 instruction -362 " 05 00 00 00 01 \n" -363 " 05 00 00 00 02 \n" -364 ); -365 CHECK_TRACE_CONTENTS( -366 "run: 0x00000001 opcode: 0f\n" -367 "run: 0x00000007 opcode: 05\n" -368 "run: 0x0000000c opcode: 05\n" -369 ); -370 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -371 } +224 :(before "End Two-Byte Opcodes Starting With 0f") +225 case 0x8d: { // jump disp32 if !SF +226 const int32_t offset = next32(); +227 if (SF == OF) { +228 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +229 EIP += offset; +230 } +231 break; +232 } +233 case 0x83: { // jump disp32 if !CF +234 const int32_t offset = next32(); +235 if (!CF) { +236 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +237 EIP += offset; +238 } +239 break; +240 } +241 +242 :(code) +243 void test_jge_disp32_fail() { +244 SF = true; +245 OF = false; +246 run( +247 "== code 0x1\n" +248 // op ModR/M SIB displacement immediate +249 " 0f 8d 05 00 00 00 \n" // skip 1 instruction +250 " 05 00 00 00 01 \n" +251 " 05 00 00 00 02 \n" +252 ); +253 CHECK_TRACE_CONTENTS( +254 "run: 0x00000001 opcode: 0f\n" +255 "run: 0x00000007 opcode: 05\n" +256 "run: 0x0000000c opcode: 05\n" +257 ); +258 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +259 } +260 +261 //:: jump if lesser +262 +263 :(before "End Initialize Op Names") +264 put_new(Name_0f, "8c", "jump disp32 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); +265 put_new(Name_0f, "82", "jump disp32 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); +266 +267 :(code) +268 void test_jl_disp32_success() { +269 ZF = false; +270 SF = true; +271 OF = false; +272 run( +273 "== code 0x1\n" +274 // op ModR/M SIB displacement immediate +275 " 0f 8c 05 00 00 00 \n" // skip 1 instruction +276 " 05 00 00 00 01 \n" +277 " 05 00 00 00 02 \n" +278 ); +279 CHECK_TRACE_CONTENTS( +280 "run: 0x00000001 opcode: 0f\n" +281 "run: jump 5\n" +282 "run: 0x0000000c opcode: 05\n" +283 ); +284 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +285 } +286 +287 :(before "End Two-Byte Opcodes Starting With 0f") +288 case 0x8c: { // jump disp32 if SF and !ZF +289 const int32_t offset = next32(); +290 if (SF != OF) { +291 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +292 EIP += offset; +293 } +294 break; +295 } +296 case 0x72: { // jump disp32 if CF +297 const int32_t offset = next32(); +298 if (CF) { +299 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +300 EIP += offset; +301 } +302 break; +303 } +304 +305 :(code) +306 void test_jl_disp32_fail() { +307 ZF = false; +308 SF = false; +309 OF = false; +310 run( +311 "== code 0x1\n" +312 // op ModR/M SIB displacement immediate +313 " 0f 8c 05 00 00 00 \n" // skip 1 instruction +314 " 05 00 00 00 01 \n" +315 " 05 00 00 00 02 \n" +316 ); +317 CHECK_TRACE_CONTENTS( +318 "run: 0x00000001 opcode: 0f\n" +319 "run: 0x00000007 opcode: 05\n" +320 "run: 0x0000000c opcode: 05\n" +321 ); +322 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +323 } +324 +325 //:: jump if lesser or equal +326 +327 :(before "End Initialize Op Names") +328 put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); +329 put_new(Name_0f, "86", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); +330 +331 :(code) +332 void test_jle_disp32_equal() { +333 ZF = true; +334 SF = false; +335 OF = false; +336 run( +337 "== code 0x1\n" +338 // op ModR/M SIB displacement immediate +339 " 0f 8e 05 00 00 00 \n" // skip 1 instruction +340 " 05 00 00 00 01 \n" +341 " 05 00 00 00 02 \n" +342 ); +343 CHECK_TRACE_CONTENTS( +344 "run: 0x00000001 opcode: 0f\n" +345 "run: jump 5\n" +346 "run: 0x0000000c opcode: 05\n" +347 ); +348 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +349 } +350 +351 :(code) +352 void test_jle_disp32_lesser() { +353 ZF = false; +354 SF = true; +355 OF = false; +356 run( +357 "== code 0x1\n" +358 // op ModR/M SIB displacement immediate +359 " 0f 8e 05 00 00 00 \n" // skip 1 instruction +360 " 05 00 00 00 01 \n" +361 " 05 00 00 00 02 \n" +362 ); +363 CHECK_TRACE_CONTENTS( +364 "run: 0x00000001 opcode: 0f\n" +365 "run: jump 5\n" +366 "run: 0x0000000c opcode: 05\n" +367 ); +368 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +369 } +370 +371 :(before "End Two-Byte Opcodes Starting With 0f") +372 case 0x8e: { // jump disp32 if SF or ZF +373 const int32_t offset = next32(); +374 if (ZF || SF != OF) { +375 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +376 EIP += offset; +377 } +378 break; +379 } +380 case 0x86: { // jump disp32 if ZF or CF +381 const int32_t offset = next32(); +382 if (ZF || CF) { +383 trace(Callstack_depth+1, "run") << "jump " << offset << end(); +384 EIP += offset; +385 } +386 break; +387 } +388 +389 :(code) +390 void test_jle_disp32_greater() { +391 ZF = false; +392 SF = false; +393 OF = false; +394 run( +395 "== code 0x1\n" +396 // op ModR/M SIB displacement immediate +397 " 0f 8e 05 00 00 00 \n" // skip 1 instruction +398 " 05 00 00 00 01 \n" +399 " 05 00 00 00 02 \n" +400 ); +401 CHECK_TRACE_CONTENTS( +402 "run: 0x00000001 opcode: 0f\n" +403 "run: 0x00000007 opcode: 05\n" +404 "run: 0x0000000c opcode: 05\n" +405 ); +406 CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +407 } diff --git a/html/subx/019functions.cc.html b/html/subx/019functions.cc.html index 013c56f0..1a1202db 100644 --- a/html/subx/019functions.cc.html +++ b/html/subx/019functions.cc.html @@ -3,8 +3,8 @@ Mu - subx/019functions.cc - - + + @@ -16,12 +16,12 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .CommentedCode { color: #8a8a8a; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } .SalientComment { color: #0000af; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -61,7 +61,7 @@ if ('onhashchange' in window) { 1 //:: call 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "e8", "call disp32 (call)"); + 4 put_new(Name, "e8", "call disp32 (call)"); 5 6 :(code) 7 void test_call_disp32() { @@ -85,11 +85,11 @@ if ('onhashchange' in window) { 25 case 0xe8: { // call disp32 relative to next EIP 26 const int32_t offset = next32(); 27 ++Callstack_depth; - 28 trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end(); + 28 trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end(); 29 //? cerr << "push: EIP: " << EIP << " => " << Reg[ESP].u << '\n'; 30 push(EIP); 31 EIP += offset; - 32 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); + 32 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 33 break; 34 } 35 @@ -121,7 +121,7 @@ if ('onhashchange' in window) { 61 const int32_t* offset = effective_address(modrm); 62 push(EIP); 63 EIP += *offset; - 64 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); + 64 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 65 ++Callstack_depth; 66 break; 67 } @@ -151,13 +151,13 @@ if ('onhashchange' in window) { 91 //:: ret 92 93 :(before "End Initialize Op Names") - 94 put_new(Name, "c3", "return from most recent unfinished call (ret)"); + 94 put_new(Name, "c3", "return from most recent unfinished call (ret)"); 95 96 :(code) 97 void test_ret() { 98 Mem.push_back(vma(0xbd000000)); // manually allocate memory 99 Reg[ESP].u = 0xbd000064; -100 write_mem_u32(Reg[ESP].u, 0x10); +100 write_mem_u32(Reg[ESP].u, 0x10); 101 run( 102 "== code 0x1\n" 103 // op ModR/M SIB displacement immediate @@ -176,8 +176,8 @@ if ('onhashchange' in window) { 116 case 0xc3: { // return from a call 117 trace(Callstack_depth+1, "run") << "return" << end(); 118 --Callstack_depth; -119 EIP = pop(); -120 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); +119 EIP = pop(); +120 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 121 break; 122 } diff --git a/html/subx/020syscalls.cc.html b/html/subx/020syscalls.cc.html index cb6b37e1..73dcef8d 100644 --- a/html/subx/020syscalls.cc.html +++ b/html/subx/020syscalls.cc.html @@ -3,8 +3,8 @@ Mu - subx/020syscalls.cc - - + + @@ -14,14 +14,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.PreProc { color: #c000c0; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } +.PreProc { color: #c000c0; } .cSpecial { color: #008000; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -59,14 +59,14 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/020syscalls.cc
   1 :(before "End Initialize Op Names")
-  2 put_new(Name, "cd", "software interrupt (int)");
+  2 put_new(Name, "cd", "software interrupt (int)");
   3 
   4 :(before "End Single-Byte Opcodes")
   5 case 0xcd: {  // int imm8 (software interrupt)
   6   trace(Callstack_depth+1, "run") << "syscall" << end();
-  7   uint8_t code = next();
+  7   uint8_t code = next();
   8   if (code != 0x80) {
-  9     raise << "Unimplemented interrupt code " << HEXBYTE << code << '\n' << end();
+  9     raise << "Unimplemented interrupt code " << HEXBYTE << code << '\n' << end();
  10     raise << "  Only `int 80h` supported for now.\n" << end();
  11     break;
  12   }
@@ -88,7 +88,7 @@ if ('onhashchange' in window) {
  28     break;
  29   case 4:
  30     trace(Callstack_depth+1, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
- 31     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
+ 31     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
  32     Reg[EAX].i = write(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
  33     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
  34     if (Reg[EAX].i == -1) raise << "write: " << strerror(errno) << '\n' << end();
@@ -97,8 +97,8 @@ if ('onhashchange' in window) {
  37     check_flags(ECX);
  38     check_mode(EDX);
  39     trace(Callstack_depth+1, "run") << "open: " << Reg[EBX].u << ' ' << Reg[ECX].u << end();
- 40     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 41     Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640);
+ 40     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 41     Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640);
  42     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
  43     if (Reg[EAX].i == -1) raise << "open: " << strerror(errno) << '\n' << end();
  44     break;
@@ -112,23 +112,23 @@ if ('onhashchange' in window) {
  52   case 8:
  53     check_mode(ECX);
  54     trace(Callstack_depth+1, "run") << "creat: " << Reg[EBX].u << end();
- 55     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 56     Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640);
+ 55     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 56     Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640);
  57     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
  58     if (Reg[EAX].i == -1) raise << "creat: " << strerror(errno) << '\n' << end();
  59     break;
  60   case 10:
  61     trace(Callstack_depth+1, "run") << "unlink: " << Reg[EBX].u << end();
- 62     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 63     Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u));
+ 62     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 63     Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u));
  64     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
  65     if (Reg[EAX].i == -1) raise << "unlink: " << strerror(errno) << '\n' << end();
  66     break;
  67   case 38:
  68     trace(Callstack_depth+1, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end();
- 69     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 70     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
- 71     Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u));
+ 69     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 70     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
+ 71     Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u));
  72     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
  73     if (Reg[EAX].i == -1) raise << "rename: " << strerror(errno) << '\n' << end();
  74     break;
@@ -144,7 +144,7 @@ if ('onhashchange' in window) {
  84     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].u << end();
  85     break;
  86   default:
- 87     raise << HEXWORD << EIP << ": unimplemented syscall " << Reg[EAX].u << '\n' << end();
+ 87     raise << HEXWORD << EIP << ": unimplemented syscall " << Reg[EAX].u << '\n' << end();
  88   }
  89 }
  90 
@@ -153,18 +153,18 @@ if ('onhashchange' in window) {
  93 void check_flags(int reg) {
  94   uint32_t flags = Reg[reg].u;
  95   if (flags != ((flags & O_RDONLY) | (flags & O_WRONLY))) {
- 96     cerr << HEXWORD << EIP << ": most POSIX flags to the open() syscall are not supported. Just O_RDONLY and O_WRONLY for now. Zero concurrent access support.\n";
+ 96     cerr << HEXWORD << EIP << ": most POSIX flags to the open() syscall are not supported. Just O_RDONLY and O_WRONLY for now. Zero concurrent access support.\n";
  97     exit(1);
  98   }
  99   if ((flags & O_RDONLY) && (flags & O_WRONLY)) {
-100     cerr << HEXWORD << EIP << ": can't open a file for both reading and writing at once. See http://man7.org/linux/man-pages/man2/open.2.html.\n";
+100     cerr << HEXWORD << EIP << ": can't open a file for both reading and writing at once. See http://man7.org/linux/man-pages/man2/open.2.html.\n";
 101     exit(1);
 102   }
 103 }
 104 
 105 void check_mode(int reg) {
 106   if (Reg[reg].u != 0600) {
-107     cerr << HEXWORD << EIP << ": SubX is oblivious to file permissions; register " << reg << " must be 0.\n";
+107     cerr << HEXWORD << EIP << ": SubX is oblivious to file permissions; register " << reg << " must be 0.\n";
 108     exit(1);
 109   }
 110 }
diff --git a/html/subx/021byte_addressing.cc.html b/html/subx/021byte_addressing.cc.html
index 26c26be4..147796a0 100644
--- a/html/subx/021byte_addressing.cc.html
+++ b/html/subx/021byte_addressing.cc.html
@@ -3,8 +3,8 @@
 
 
 Mu - subx/021byte_addressing.cc
-
-
+
+
 
 
 
@@ -16,11 +16,11 @@ a { color:inherit; }
 * { font-size:12pt; font-size: 1em; }
 .LineNr { }
 .Constant { color: #008787; }
+.Comment { color: #005faf; }
 .Delimiter { color: #c000c0; }
 .Special { color: #d70000; }
 .Identifier { color: #af5f00; }
 .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; }
-.Comment { color: #005faf; }
 .cSpecial { color: #008000; }
 -->
 
@@ -38,7 +38,7 @@ function JumpToLine()
   if (lineNum.indexOf('L') == -1) {
     lineNum = 'L'+lineNum;
   }
-  lineElem = document.getElementById(lineNum);
+  var lineElem = document.getElementById(lineNum);
   /* Always jump to new location even if the line was hidden inside a fold, or
    * we corrected the raw number to a line ID.
    */
@@ -97,7 +97,7 @@ if ('onhashchange' in window) {
  38 }
  39 
  40 :(before "End Initialize Op Names")
- 41 put_new(Name, "88", "copy r8 to r8/m8-at-r32");
+ 41 put_new(Name, "88", "copy r8 to r8/m8-at-r32");
  42 
  43 :(code)
  44 void test_copy_r8_to_mem_at_r32() {
@@ -121,21 +121,21 @@ if ('onhashchange' in window) {
  62 
  63 :(before "End Single-Byte Opcodes")
  64 case 0x88: {  // copy r8 to r/m8
- 65   const uint8_t modrm = next();
+ 65   const uint8_t modrm = next();
  66   const uint8_t rsrc = (modrm>>3)&0x7;
  67   trace(Callstack_depth+1, "run") << "copy " << rname_8bit(rsrc) << " to r8/m8-at-r32" << end();
  68   // use unsigned to zero-extend 8-bit value to 32 bits
  69   uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
  70   const uint8_t* src = reg_8bit(rsrc);
  71   *dest = *src;
- 72   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
+ 72   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
  73   break;
  74 }
  75 
  76 //:
  77 
  78 :(before "End Initialize Op Names")
- 79 put_new(Name, "8a", "copy r8/m8-at-r32 to r8");
+ 79 put_new(Name, "8a", "copy r8/m8-at-r32 to r8");
  80 
  81 :(code)
  82 void test_copy_mem_at_r32_to_r8() {
@@ -160,16 +160,16 @@ if ('onhashchange' in window) {
 101 
 102 :(before "End Single-Byte Opcodes")
 103 case 0x8a: {  // copy r/m8 to r8
-104   const uint8_t modrm = next();
+104   const uint8_t modrm = next();
 105   const uint8_t rdest = (modrm>>3)&0x7;
 106   trace(Callstack_depth+1, "run") << "copy r8/m8-at-r32 to " << rname_8bit(rdest) << end();
 107   // use unsigned to zero-extend 8-bit value to 32 bits
 108   const uint8_t* src = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
 109   uint8_t* dest = reg_8bit(rdest);
-110   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
+110   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
 111   *dest = *src;
 112   const uint8_t rdest_32bit = rdest & 0x3;
-113   trace(Callstack_depth+1, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end();
+113   trace(Callstack_depth+1, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end();
 114   break;
 115 }
 116 
@@ -195,7 +195,7 @@ if ('onhashchange' in window) {
 136 //:
 137 
 138 :(before "End Initialize Op Names")
-139 put_new(Name, "c6", "copy imm8 to r8/m8-at-r32 (mov)");
+139 put_new(Name, "c6", "copy imm8 to r8/m8-at-r32 (mov)");
 140 
 141 :(code)
 142 void test_copy_imm8_to_mem_at_r32() {
@@ -218,19 +218,19 @@ if ('onhashchange' in window) {
 159 
 160 :(before "End Single-Byte Opcodes")
 161 case 0xc6: {  // copy imm8 to r/m8
-162   const uint8_t modrm = next();
-163   const uint8_t src = next();
+162   const uint8_t modrm = next();
+163   const uint8_t src = next();
 164   trace(Callstack_depth+1, "run") << "copy imm8 to r8/m8-at-r32" << end();
-165   trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << src << end();
+165   trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << NUM(src) << end();
 166   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
 167   if (subop != 0) {
-168     cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n";
+168     cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n";
 169     exit(1);
 170   }
 171   // use unsigned to zero-extend 8-bit value to 32 bits
 172   uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
 173   *dest = src;
-174   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
+174   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
 175   break;
 176 }
 
diff --git a/html/subx/022div.cc.html b/html/subx/022div.cc.html index bb3bce25..58b74e12 100644 --- a/html/subx/022div.cc.html +++ b/html/subx/022div.cc.html @@ -3,8 +3,8 @@ Mu - subx/022div.cc - - + + @@ -15,12 +15,12 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } --> @@ -37,7 +37,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -59,7 +59,7 @@ if ('onhashchange' in window) { 1 //: helper for division operations: sign-extend EAX into EDX 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "99", "sign-extend EAX into EDX (cdq)"); + 4 put_new(Name, "99", "sign-extend EAX into EDX (cdq)"); 5 6 :(code) 7 void test_cdq() { @@ -78,7 +78,7 @@ if ('onhashchange' in window) { 20 case 0x99: { // sign-extend EAX into EDX 21 trace(Callstack_depth+1, "run") << "sign-extend EAX into EDX" << end(); 22 Reg[EDX].i = (Reg[EAX].i < 0) ? -1 : 0; -23 trace(Callstack_depth+1, "run") << "EDX is now 0x" << HEXWORD << Reg[EDX].u << end(); +23 trace(Callstack_depth+1, "run") << "EDX is now 0x" << HEXWORD << Reg[EDX].u << end(); 24 break; 25 } 26 diff --git a/html/subx/028translate.cc.html b/html/subx/028translate.cc.html index bf79ea7a..be89d953 100644 --- a/html/subx/028translate.cc.html +++ b/html/subx/028translate.cc.html @@ -3,8 +3,8 @@ Mu - subx/028translate.cc - - + + @@ -14,15 +14,15 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.PreProc { color: #c000c0; } .LineNr { } -.Constant { color: #008787; } .SalientComment { color: #0000af; } +.Constant { color: #008787; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.PreProc { color: #c000c0; } .cSpecial { color: #008000; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -227,7 +227,7 @@ if ('onhashchange' in window) { 166 uint32_t p_align = 0x1000; // default page size on linux 167 emit(p_align); 168 if (p_offset % p_align != p_start % p_align) { -169 raise << "segment starting at 0x" << HEXWORD << p_start << " is improperly aligned; alignment for p_offset " << p_offset << " should be " << (p_offset % p_align) << " but is " << (p_start % p_align) << '\n' << end(); +169 raise << "segment starting at 0x" << HEXWORD << p_start << " is improperly aligned; alignment for p_offset " << p_offset << " should be " << (p_offset % p_align) << " but is " << (p_start % p_align) << '\n' << end(); 170 return; 171 } 172 diff --git a/html/subx/029transforms.cc.html b/html/subx/029transforms.cc.html index dcfd286b..75383daa 100644 --- a/html/subx/029transforms.cc.html +++ b/html/subx/029transforms.cc.html @@ -3,8 +3,8 @@ Mu - subx/029transforms.cc - - + + @@ -15,8 +15,8 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } -.Delimiter { color: #c000c0; } .Comment { color: #005faf; } +.Delimiter { color: #c000c0; } --> @@ -33,7 +33,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/030---operands.cc.html b/html/subx/030---operands.cc.html index c1e951a2..8aa1af86 100644 --- a/html/subx/030---operands.cc.html +++ b/html/subx/030---operands.cc.html @@ -3,8 +3,8 @@ Mu - subx/030---operands.cc - - + + @@ -15,12 +15,12 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } -.Constant { color: #008787; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Constant { color: #008787; } .SalientComment { color: #0000af; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -352,7 +352,7 @@ if ('onhashchange' in window) { 293 string hex_byte_to_string(uint8_t val) { 294 ostringstream out; 295 // uint8_t prints without padding, but int8_t will expand to 32 bits again -296 out << HEXBYTE << NUM(val); +296 out << HEXBYTE << NUM(val); 297 return out.str(); 298 } 299 diff --git a/html/subx/031check_operands.cc.html b/html/subx/031check_operands.cc.html index a42fdd15..32bf5c71 100644 --- a/html/subx/031check_operands.cc.html +++ b/html/subx/031check_operands.cc.html @@ -3,8 +3,8 @@ Mu - subx/031check_operands.cc - - + + @@ -17,13 +17,13 @@ a { color:inherit; } .CommentedCode { color: #8a8a8a; } .LineNr { } .Constant { color: #008787; } -.SalientComment { color: #0000af; } -.Delimiter { color: #c000c0; } +.Comment { color: #005faf; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Delimiter { color: #c000c0; } .PreProc { color: #c000c0; } +.SalientComment { color: #0000af; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -93,14 +93,14 @@ if ('onhashchange' in window) { 32 return; 33 } 34 if (op.data == "f3") { - 35 check_operands_f3(inst); + 35 check_operands_f3(inst); 36 return; 37 } 38 check_operands(inst, op); 39 } 40 41 word preprocess_op(word/*copy*/ op) { - 42 op.data = tolower(op.data.c_str()); + 42 op.data = tolower(op.data.c_str()); 43 // opcodes can't be negative 44 if (starts_with(op.data, "0x")) 45 op.data = op.data.substr(2); @@ -286,467 +286,470 @@ if ('onhashchange' in window) { 225 put(Permitted_operands, "87", 0x01); 226 // copy address (lea) 227 put(Permitted_operands, "8d", 0x01); -228 // pop -229 put(Permitted_operands, "8f", 0x01); -230 -231 //// Class N: op, ModR/M and subop (not r32) -232 // imm32 imm8 disp32 |disp16 disp8 subop modrm -233 // 0 0 0 |0 0 1 1 -234 put(Permitted_operands, "d3", 0x03); // shift -235 put(Permitted_operands, "f7", 0x03); // test/not/mul/div -236 put(Permitted_operands, "ff", 0x03); // jump/push/call -237 -238 //// Class O: op, ModR/M, subop (not r32) and imm8 -239 // imm32 imm8 disp32 |disp16 disp8 subop modrm -240 // 0 1 0 |0 0 1 1 -241 put(Permitted_operands, "c1", 0x23); // combine -242 put(Permitted_operands, "c6", 0x23); // copy -243 -244 //// Class P: op, ModR/M, subop (not r32) and imm32 -245 // imm32 imm8 disp32 |disp16 disp8 subop modrm -246 // 1 0 0 |0 0 1 1 -247 put(Permitted_operands, "81", 0x43); // combine -248 put(Permitted_operands, "c7", 0x43); // copy -249 -250 // End Init Permitted Operands -251 } -252 -253 #define HAS(bitvector, bit) ((bitvector) & (1 << (bit))) -254 #define SET(bitvector, bit) ((bitvector) | (1 << (bit))) -255 #define CLEAR(bitvector, bit) ((bitvector) & (~(1 << (bit)))) -256 -257 void check_operands(const line& inst, const word& op) { -258 if (!is_hex_byte(op)) return; -259 uint8_t expected_bitvector = get(Permitted_operands, op.data); -260 if (HAS(expected_bitvector, MODRM)) { -261 check_operands_modrm(inst, op); -262 compare_bitvector_modrm(inst, expected_bitvector, op); -263 } -264 else { -265 compare_bitvector(inst, expected_bitvector, op); -266 } -267 } -268 -269 //: Many instructions can be checked just by comparing bitvectors. -270 -271 void compare_bitvector(const line& inst, uint8_t expected, const word& op) { -272 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -273 uint8_t bitvector = compute_expected_operand_bitvector(inst); -274 if (trace_contains_errors()) return; // duplicate operand type -275 if (bitvector == expected) return; // all good with this instruction -276 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -277 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; -278 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand -279 const string& optype = Operand_type_name.at(i); -280 if ((bitvector & 0x1) > (expected & 0x1)) -281 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); -282 else -283 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); -284 // continue giving all errors for a single instruction -285 } -286 // ignore settings in any unused bits -287 } -288 -289 string maybe_name(const word& op) { -290 if (!is_hex_byte(op)) return ""; -291 if (!contains_key(Name, op.data)) return ""; -292 // strip stuff in parens from the name -293 const string& s = get(Name, op.data); -294 return " ("+s.substr(0, s.find(" ("))+')'; -295 } -296 -297 uint32_t compute_expected_operand_bitvector(const line& inst) { -298 set<string> operands_found; -299 uint32_t bitvector = 0; -300 for (int i = /*skip op*/1; i < SIZE(inst.words); ++i) { -301 bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i), operands_found, inst); -302 if (trace_contains_errors()) return INVALID_OPERANDS; // duplicate operand type -303 } -304 return bitvector; -305 } -306 -307 bool has_operands(const line& inst) { -308 return SIZE(inst.words) > first_operand(inst); -309 } -310 -311 int first_operand(const line& inst) { -312 if (inst.words.at(0).data == "0f") return 2; -313 if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") { -314 if (inst.words.at(1).data == "0f") -315 return 3; -316 else -317 return 2; -318 } -319 return 1; -320 } -321 -322 // Scan the metadata of 'w' and return the expected bit corresponding to any operand type. -323 // Also raise an error if metadata contains multiple operand types. -324 uint32_t expected_bit_for_received_operand(const word& w, set<string>& instruction_operands, const line& inst) { -325 uint32_t bv = 0; -326 bool found = false; -327 for (int i = 0; i < SIZE(w.metadata); ++i) { -328 string/*copy*/ curr = w.metadata.at(i); -329 string expected_metadata = curr; -330 if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "scale" || curr == "index" || curr == "base") -331 expected_metadata = "modrm"; -332 else if (!contains_key(Operand_type, curr)) continue; // ignore unrecognized metadata -333 if (found) { -334 raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end(); -335 return INVALID_OPERANDS; -336 } -337 if (instruction_operands.find(curr) != instruction_operands.end()) { -338 raise << "'" << to_string(inst) << "': duplicate " << curr << " operand\n" << end(); -339 return INVALID_OPERANDS; -340 } -341 instruction_operands.insert(curr); -342 bv = (1 << get(Operand_type, expected_metadata)); -343 found = true; -344 } -345 return bv; -346 } -347 -348 void test_conflicting_operand_type() { -349 Hide_errors = true; -350 run( -351 "== code 0x1\n" -352 "cd/software-interrupt 80/imm8/imm32\n" -353 ); -354 CHECK_TRACE_CONTENTS( -355 "error: '80/imm8/imm32' has conflicting operand types; it should have only one\n" -356 ); -357 } -358 -359 //: Instructions computing effective addresses have more complex rules, so -360 //: we'll hard-code a common set of instruction-decoding rules. -361 -362 void test_check_missing_mod_operand() { -363 Hide_errors = true; -364 run( -365 "== code 0x1\n" -366 "81 0/add/subop 3/rm32/ebx 1/imm32\n" -367 ); -368 CHECK_TRACE_CONTENTS( -369 "error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod operand\n" -370 ); -371 } -372 -373 void check_operands_modrm(const line& inst, const word& op) { -374 if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -375 check_operand_metadata_present(inst, "mod", op); -376 check_operand_metadata_present(inst, "rm32", op); -377 // no check for r32; some instructions don't use it; just assume it's 0 if missing -378 if (op.data == "81" || op.data == "8f" || op.data == "ff") { // keep sync'd with 'help subop' -379 check_operand_metadata_present(inst, "subop", op); -380 check_operand_metadata_absent(inst, "r32", op, "should be replaced by subop"); -381 } -382 if (trace_contains_errors()) return; -383 if (metadata(inst, "rm32").data != "4") return; -384 // SIB byte checks -385 uint8_t mod = hex_byte(metadata(inst, "mod").data); -386 if (mod != /*direct*/3) { -387 check_operand_metadata_present(inst, "base", op); -388 check_operand_metadata_present(inst, "index", op); // otherwise why go to SIB? -389 } -390 else { -391 check_operand_metadata_absent(inst, "base", op, "direct mode"); -392 check_operand_metadata_absent(inst, "index", op, "direct mode"); -393 } -394 // no check for scale; 0 (2**0 = 1) by default -395 } -396 -397 // same as compare_bitvector, with one additional exception for modrm-based -398 // instructions: they may use an extra displacement on occasion -399 void compare_bitvector_modrm(const line& inst, uint8_t expected, const word& op) { -400 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -401 uint8_t bitvector = compute_expected_operand_bitvector(inst); -402 if (trace_contains_errors()) return; // duplicate operand type -403 // update 'expected' bitvector for the additional exception -404 if (has_operand_metadata(inst, "mod")) { -405 int32_t mod = parse_int(metadata(inst, "mod").data); -406 switch (mod) { -407 case 0: -408 if (has_operand_metadata(inst, "rm32") && parse_int(metadata(inst, "rm32").data) == 5) -409 expected |= (1<<DISP32); -410 break; -411 case 1: -412 expected |= (1<<DISP8); -413 break; -414 case 2: -415 expected |= (1<<DISP32); -416 break; -417 } -418 } -419 if (bitvector == expected) return; // all good with this instruction -420 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -421 //? cerr << "comparing for modrm " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; -422 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand -423 const string& optype = Operand_type_name.at(i); -424 if ((bitvector & 0x1) > (expected & 0x1)) -425 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); -426 else -427 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); -428 // continue giving all errors for a single instruction -429 } -430 // ignore settings in any unused bits -431 } -432 -433 void check_operand_metadata_present(const line& inst, const string& type, const word& op) { -434 if (!has_operand_metadata(inst, type)) -435 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << type << " operand\n" << end(); -436 } -437 -438 void check_operand_metadata_absent(const line& inst, const string& type, const word& op, const string& msg) { -439 if (has_operand_metadata(inst, type)) -440 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << type << " operand (" << msg << ")\n" << end(); -441 } -442 -443 void test_modrm_with_displacement() { -444 Reg[EAX].u = 0x1; -445 transform( -446 "== code 0x1\n" -447 // just avoid null pointer -448 "8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8\n" // copy *(EAX+4) to EDX -449 ); -450 CHECK_TRACE_COUNT("error", 0); -451 } -452 -453 void test_check_missing_disp8() { -454 Hide_errors = true; -455 transform( -456 "== code 0x1\n" -457 "89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX\n" // missing disp8 -458 ); -459 CHECK_TRACE_CONTENTS( -460 "error: '89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX' (copy r32 to rm32): missing disp8 operand\n" -461 ); -462 } -463 -464 void test_check_missing_disp32() { -465 Hide_errors = true; -466 transform( -467 "== code 0x1\n" -468 "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX\n" // missing disp32 -469 ); -470 CHECK_TRACE_CONTENTS( -471 "error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand\n" -472 ); -473 } -474 -475 void test_conflicting_operands_in_modrm_instruction() { -476 Hide_errors = true; -477 run( -478 "== code 0x1\n" -479 "01/add 0/mod 3/mod\n" -480 ); -481 CHECK_TRACE_CONTENTS( -482 "error: '01/add 0/mod 3/mod' has conflicting mod operands\n" -483 ); -484 } -485 -486 void test_conflicting_operand_type_modrm() { -487 Hide_errors = true; -488 run( -489 "== code 0x1\n" -490 "01/add 0/mod 3/rm32/r32\n" -491 ); -492 CHECK_TRACE_CONTENTS( -493 "error: '3/rm32/r32' has conflicting operand types; it should have only one\n" -494 ); -495 } -496 -497 void test_check_missing_rm32_operand() { -498 Hide_errors = true; -499 run( -500 "== code 0x1\n" -501 "81 0/add/subop 0/mod 1/imm32\n" -502 ); -503 CHECK_TRACE_CONTENTS( -504 "error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 operand\n" -505 ); -506 } -507 -508 void test_check_missing_subop_operand() { -509 Hide_errors = true; -510 run( -511 "== code 0x1\n" -512 "81 0/mod 3/rm32/ebx 1/imm32\n" -513 ); -514 CHECK_TRACE_CONTENTS( -515 "error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop operand\n" -516 ); -517 } -518 -519 void test_check_missing_base_operand() { -520 Hide_errors = true; -521 run( -522 "== code 0x1\n" -523 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32\n" -524 ); -525 CHECK_TRACE_CONTENTS( -526 "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" -527 ); -528 } -529 -530 void test_check_missing_index_operand() { -531 Hide_errors = true; -532 run( -533 "== code 0x1\n" -534 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32\n" -535 ); -536 CHECK_TRACE_CONTENTS( -537 "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index operand\n" -538 ); -539 } -540 -541 void test_check_missing_base_operand_2() { -542 Hide_errors = true; -543 run( -544 "== code 0x1\n" -545 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32\n" -546 ); -547 CHECK_TRACE_CONTENTS( -548 "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" -549 ); -550 } -551 -552 void test_check_extra_displacement() { -553 Hide_errors = true; -554 run( -555 "== code 0x1\n" -556 "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8\n" -557 ); -558 CHECK_TRACE_CONTENTS( -559 "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand\n" -560 ); -561 } -562 -563 void test_check_duplicate_operand() { -564 Hide_errors = true; -565 run( -566 "== code 0x1\n" -567 "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32\n" -568 ); -569 CHECK_TRACE_CONTENTS( -570 "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32': duplicate r32 operand\n" -571 ); -572 } -573 -574 void test_check_base_operand_not_needed_in_direct_mode() { -575 run( -576 "== code 0x1\n" -577 "81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32\n" -578 ); -579 CHECK_TRACE_COUNT("error", 0); -580 } -581 -582 void test_extra_modrm() { -583 Hide_errors = true; -584 run( -585 "== code 0x1\n" -586 "59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP\n" -587 ); -588 CHECK_TRACE_CONTENTS( -589 "error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand\n" -590 ); -591 } -592 -593 //:: similarly handle multi-byte opcodes -594 -595 void check_operands_0f(const line& inst) { -596 assert(inst.words.at(0).data == "0f"); -597 if (SIZE(inst.words) == 1) { -598 raise << "opcode '0f' requires a second opcode\n" << end(); -599 return; -600 } -601 word op = preprocess_op(inst.words.at(1)); -602 if (!contains_key(Name_0f, op.data)) { -603 raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end(); -604 return; -605 } -606 check_operands_0f(inst, op); -607 } -608 -609 void check_operands_f3(const line& /*unused*/) { -610 raise << "no supported opcodes starting with f3\n" << end(); -611 } -612 -613 void test_check_missing_disp32_operand() { -614 Hide_errors = true; -615 run( -616 "== code 0x1\n" -617 " 0f 84 # jmp if ZF to ??\n" -618 ); -619 CHECK_TRACE_CONTENTS( -620 "error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand\n" -621 ); -622 } -623 -624 :(before "End Globals") -625 map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f; -626 :(before "End Init Permitted Operands") -627 //// Class D: just op and disp32 -628 // imm32 imm8 disp32 |disp16 disp8 subop modrm -629 // 0 0 1 |0 0 0 0 -630 put_new(Permitted_operands_0f, "84", 0x10); -631 put_new(Permitted_operands_0f, "85", 0x10); -632 put_new(Permitted_operands_0f, "8c", 0x10); -633 put_new(Permitted_operands_0f, "8d", 0x10); -634 put_new(Permitted_operands_0f, "8e", 0x10); -635 put_new(Permitted_operands_0f, "8f", 0x10); -636 -637 //// Class M: using ModR/M byte -638 // imm32 imm8 disp32 |disp16 disp8 subop modrm -639 // 0 0 0 |0 0 0 1 -640 put_new(Permitted_operands_0f, "af", 0x01); -641 -642 :(code) -643 void check_operands_0f(const line& inst, const word& op) { -644 uint8_t expected_bitvector = get(Permitted_operands_0f, op.data); -645 if (HAS(expected_bitvector, MODRM)) -646 check_operands_modrm(inst, op); -647 compare_bitvector_0f(inst, CLEAR(expected_bitvector, MODRM), op); -648 } -649 -650 void compare_bitvector_0f(const line& inst, uint8_t expected, const word& op) { -651 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -652 uint8_t bitvector = compute_expected_operand_bitvector(inst); -653 if (trace_contains_errors()) return; // duplicate operand type -654 if (bitvector == expected) return; // all good with this instruction -655 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -656 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; -657 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand -658 const string& optype = Operand_type_name.at(i); -659 if ((bitvector & 0x1) > (expected & 0x1)) -660 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": unexpected " << optype << " operand\n" << end(); -661 else -662 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": missing " << optype << " operand\n" << end(); -663 // continue giving all errors for a single instruction -664 } -665 // ignore settings in any unused bits -666 } -667 -668 string maybe_name_0f(const word& op) { -669 if (!is_hex_byte(op)) return ""; -670 if (!contains_key(Name_0f, op.data)) return ""; -671 // strip stuff in parens from the name -672 const string& s = get(Name_0f, op.data); -673 return " ("+s.substr(0, s.find(" ("))+')'; -674 } -675 -676 string tolower(const char* s) { -677 ostringstream out; -678 for (/*nada*/; *s; ++s) -679 out << static_cast<char>(tolower(*s)); -680 return out.str(); -681 } -682 -683 #undef HAS -684 #undef SET -685 #undef CLEAR -686 -687 :(before "End Includes") -688 #include<cctype> +228 +229 //// Class N: op, ModR/M and subop (not r32) +230 // imm32 imm8 disp32 |disp16 disp8 subop modrm +231 // 0 0 0 |0 0 1 1 +232 put(Permitted_operands, "8f", 0x03); // pop +233 put(Permitted_operands, "d3", 0x03); // shift +234 put(Permitted_operands, "f7", 0x03); // test/not/mul/div +235 put(Permitted_operands, "ff", 0x03); // jump/push/call +236 +237 //// Class O: op, ModR/M, subop (not r32) and imm8 +238 // imm32 imm8 disp32 |disp16 disp8 subop modrm +239 // 0 1 0 |0 0 1 1 +240 put(Permitted_operands, "c1", 0x23); // combine +241 put(Permitted_operands, "c6", 0x23); // copy +242 +243 //// Class P: op, ModR/M, subop (not r32) and imm32 +244 // imm32 imm8 disp32 |disp16 disp8 subop modrm +245 // 1 0 0 |0 0 1 1 +246 put(Permitted_operands, "81", 0x43); // combine +247 put(Permitted_operands, "c7", 0x43); // copy +248 +249 // End Init Permitted Operands +250 } +251 +252 #define HAS(bitvector, bit) ((bitvector) & (1 << (bit))) +253 #define SET(bitvector, bit) ((bitvector) | (1 << (bit))) +254 #define CLEAR(bitvector, bit) ((bitvector) & (~(1 << (bit)))) +255 +256 void check_operands(const line& inst, const word& op) { +257 if (!is_hex_byte(op)) return; +258 uint8_t expected_bitvector = get(Permitted_operands, op.data); +259 if (HAS(expected_bitvector, MODRM)) { +260 check_operands_modrm(inst, op); +261 compare_bitvector_modrm(inst, expected_bitvector, op); +262 } +263 else { +264 compare_bitvector(inst, expected_bitvector, op); +265 } +266 } +267 +268 //: Many instructions can be checked just by comparing bitvectors. +269 +270 void compare_bitvector(const line& inst, uint8_t expected, const word& op) { +271 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +272 uint8_t bitvector = compute_expected_operand_bitvector(inst); +273 if (trace_contains_errors()) return; // duplicate operand type +274 if (bitvector == expected) return; // all good with this instruction +275 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { +276 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; +277 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand +278 const string& optype = Operand_type_name.at(i); +279 if ((bitvector & 0x1) > (expected & 0x1)) +280 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); +281 else +282 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); +283 // continue giving all errors for a single instruction +284 } +285 // ignore settings in any unused bits +286 } +287 +288 string maybe_name(const word& op) { +289 if (!is_hex_byte(op)) return ""; +290 if (!contains_key(Name, op.data)) return ""; +291 // strip stuff in parens from the name +292 const string& s = get(Name, op.data); +293 return " ("+s.substr(0, s.find(" ("))+')'; +294 } +295 +296 uint32_t compute_expected_operand_bitvector(const line& inst) { +297 set<string> operands_found; +298 uint32_t bitvector = 0; +299 for (int i = /*skip op*/1; i < SIZE(inst.words); ++i) { +300 bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i), operands_found, inst); +301 if (trace_contains_errors()) return INVALID_OPERANDS; // duplicate operand type +302 } +303 return bitvector; +304 } +305 +306 bool has_operands(const line& inst) { +307 return SIZE(inst.words) > first_operand(inst); +308 } +309 +310 int first_operand(const line& inst) { +311 if (inst.words.at(0).data == "0f") return 2; +312 if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") { +313 if (inst.words.at(1).data == "0f") +314 return 3; +315 else +316 return 2; +317 } +318 return 1; +319 } +320 +321 // Scan the metadata of 'w' and return the expected bit corresponding to any operand type. +322 // Also raise an error if metadata contains multiple operand types. +323 uint32_t expected_bit_for_received_operand(const word& w, set<string>& instruction_operands, const line& inst) { +324 uint32_t bv = 0; +325 bool found = false; +326 for (int i = 0; i < SIZE(w.metadata); ++i) { +327 string/*copy*/ curr = w.metadata.at(i); +328 string expected_metadata = curr; +329 if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "scale" || curr == "index" || curr == "base") +330 expected_metadata = "modrm"; +331 else if (!contains_key(Operand_type, curr)) continue; // ignore unrecognized metadata +332 if (found) { +333 raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end(); +334 return INVALID_OPERANDS; +335 } +336 if (instruction_operands.find(curr) != instruction_operands.end()) { +337 raise << "'" << to_string(inst) << "': duplicate " << curr << " operand\n" << end(); +338 return INVALID_OPERANDS; +339 } +340 instruction_operands.insert(curr); +341 bv = (1 << get(Operand_type, expected_metadata)); +342 found = true; +343 } +344 return bv; +345 } +346 +347 void test_conflicting_operand_type() { +348 Hide_errors = true; +349 run( +350 "== code 0x1\n" +351 "cd/software-interrupt 80/imm8/imm32\n" +352 ); +353 CHECK_TRACE_CONTENTS( +354 "error: '80/imm8/imm32' has conflicting operand types; it should have only one\n" +355 ); +356 } +357 +358 //: Instructions computing effective addresses have more complex rules, so +359 //: we'll hard-code a common set of instruction-decoding rules. +360 +361 void test_check_missing_mod_operand() { +362 Hide_errors = true; +363 run( +364 "== code 0x1\n" +365 "81 0/add/subop 3/rm32/ebx 1/imm32\n" +366 ); +367 CHECK_TRACE_CONTENTS( +368 "error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod operand\n" +369 ); +370 } +371 +372 void check_operands_modrm(const line& inst, const word& op) { +373 if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +374 check_operand_metadata_present(inst, "mod", op); +375 check_operand_metadata_present(inst, "rm32", op); +376 // no check for r32; some instructions don't use it; just assume it's 0 if missing +377 if (op.data == "81" || op.data == "8f" || op.data == "ff") { // keep sync'd with 'help subop' +378 check_operand_metadata_present(inst, "subop", op); +379 check_operand_metadata_absent(inst, "r32", op, "should be replaced by subop"); +380 } +381 if (trace_contains_errors()) return; +382 if (metadata(inst, "rm32").data != "4") return; +383 // SIB byte checks +384 uint8_t mod = hex_byte(metadata(inst, "mod").data); +385 if (mod != /*direct*/3) { +386 check_operand_metadata_present(inst, "base", op); +387 check_operand_metadata_present(inst, "index", op); // otherwise why go to SIB? +388 } +389 else { +390 check_operand_metadata_absent(inst, "base", op, "direct mode"); +391 check_operand_metadata_absent(inst, "index", op, "direct mode"); +392 } +393 // no check for scale; 0 (2**0 = 1) by default +394 } +395 +396 // same as compare_bitvector, with one additional exception for modrm-based +397 // instructions: they may use an extra displacement on occasion +398 void compare_bitvector_modrm(const line& inst, uint8_t expected, const word& op) { +399 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +400 uint8_t bitvector = compute_expected_operand_bitvector(inst); +401 if (trace_contains_errors()) return; // duplicate operand type +402 // update 'expected' bitvector for the additional exception +403 if (has_operand_metadata(inst, "mod")) { +404 int32_t mod = parse_int(metadata(inst, "mod").data); +405 switch (mod) { +406 case 0: +407 if (has_operand_metadata(inst, "rm32") && parse_int(metadata(inst, "rm32").data) == 5) +408 expected |= (1<<DISP32); +409 break; +410 case 1: +411 expected |= (1<<DISP8); +412 break; +413 case 2: +414 expected |= (1<<DISP32); +415 break; +416 } +417 } +418 if (bitvector == expected) return; // all good with this instruction +419 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { +420 //? cerr << "comparing for modrm " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; +421 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand +422 const string& optype = Operand_type_name.at(i); +423 if ((bitvector & 0x1) > (expected & 0x1)) +424 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); +425 else +426 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); +427 // continue giving all errors for a single instruction +428 } +429 // ignore settings in any unused bits +430 } +431 +432 void check_operand_metadata_present(const line& inst, const string& type, const word& op) { +433 if (!has_operand_metadata(inst, type)) +434 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << type << " operand\n" << end(); +435 } +436 +437 void check_operand_metadata_absent(const line& inst, const string& type, const word& op, const string& msg) { +438 if (has_operand_metadata(inst, type)) +439 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << type << " operand (" << msg << ")\n" << end(); +440 } +441 +442 void test_modrm_with_displacement() { +443 Reg[EAX].u = 0x1; +444 transform( +445 "== code 0x1\n" +446 // just avoid null pointer +447 "8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8\n" // copy *(EAX+4) to EDX +448 ); +449 CHECK_TRACE_COUNT("error", 0); +450 } +451 +452 void test_check_missing_disp8() { +453 Hide_errors = true; +454 transform( +455 "== code 0x1\n" +456 "89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX\n" // missing disp8 +457 ); +458 CHECK_TRACE_CONTENTS( +459 "error: '89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX' (copy r32 to rm32): missing disp8 operand\n" +460 ); +461 } +462 +463 void test_check_missing_disp32() { +464 Hide_errors = true; +465 transform( +466 "== code 0x1\n" +467 "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX\n" // missing disp32 +468 ); +469 CHECK_TRACE_CONTENTS( +470 "error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand\n" +471 ); +472 } +473 +474 void test_conflicting_operands_in_modrm_instruction() { +475 Hide_errors = true; +476 run( +477 "== code 0x1\n" +478 "01/add 0/mod 3/mod\n" +479 ); +480 CHECK_TRACE_CONTENTS( +481 "error: '01/add 0/mod 3/mod' has conflicting mod operands\n" +482 ); +483 } +484 +485 void test_conflicting_operand_type_modrm() { +486 Hide_errors = true; +487 run( +488 "== code 0x1\n" +489 "01/add 0/mod 3/rm32/r32\n" +490 ); +491 CHECK_TRACE_CONTENTS( +492 "error: '3/rm32/r32' has conflicting operand types; it should have only one\n" +493 ); +494 } +495 +496 void test_check_missing_rm32_operand() { +497 Hide_errors = true; +498 run( +499 "== code 0x1\n" +500 "81 0/add/subop 0/mod 1/imm32\n" +501 ); +502 CHECK_TRACE_CONTENTS( +503 "error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 operand\n" +504 ); +505 } +506 +507 void test_check_missing_subop_operand() { +508 Hide_errors = true; +509 run( +510 "== code 0x1\n" +511 "81 0/mod 3/rm32/ebx 1/imm32\n" +512 ); +513 CHECK_TRACE_CONTENTS( +514 "error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop operand\n" +515 ); +516 } +517 +518 void test_check_missing_base_operand() { +519 Hide_errors = true; +520 run( +521 "== code 0x1\n" +522 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32\n" +523 ); +524 CHECK_TRACE_CONTENTS( +525 "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" +526 ); +527 } +528 +529 void test_check_missing_index_operand() { +530 Hide_errors = true; +531 run( +532 "== code 0x1\n" +533 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32\n" +534 ); +535 CHECK_TRACE_CONTENTS( +536 "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index operand\n" +537 ); +538 } +539 +540 void test_check_missing_base_operand_2() { +541 Hide_errors = true; +542 run( +543 "== code 0x1\n" +544 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32\n" +545 ); +546 CHECK_TRACE_CONTENTS( +547 "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" +548 ); +549 } +550 +551 void test_check_extra_displacement() { +552 Hide_errors = true; +553 run( +554 "== code 0x1\n" +555 "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8\n" +556 ); +557 CHECK_TRACE_CONTENTS( +558 "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand\n" +559 ); +560 } +561 +562 void test_check_duplicate_operand() { +563 Hide_errors = true; +564 run( +565 "== code 0x1\n" +566 "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32\n" +567 ); +568 CHECK_TRACE_CONTENTS( +569 "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32': duplicate r32 operand\n" +570 ); +571 } +572 +573 void test_check_base_operand_not_needed_in_direct_mode() { +574 run( +575 "== code 0x1\n" +576 "81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32\n" +577 ); +578 CHECK_TRACE_COUNT("error", 0); +579 } +580 +581 void test_extra_modrm() { +582 Hide_errors = true; +583 run( +584 "== code 0x1\n" +585 "59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP\n" +586 ); +587 CHECK_TRACE_CONTENTS( +588 "error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand\n" +589 ); +590 } +591 +592 //:: similarly handle multi-byte opcodes +593 +594 void check_operands_0f(const line& inst) { +595 assert(inst.words.at(0).data == "0f"); +596 if (SIZE(inst.words) == 1) { +597 raise << "opcode '0f' requires a second opcode\n" << end(); +598 return; +599 } +600 word op = preprocess_op(inst.words.at(1)); +601 if (!contains_key(Name_0f, op.data)) { +602 raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end(); +603 return; +604 } +605 check_operands_0f(inst, op); +606 } +607 +608 void check_operands_f3(const line& /*unused*/) { +609 raise << "no supported opcodes starting with f3\n" << end(); +610 } +611 +612 void test_check_missing_disp32_operand() { +613 Hide_errors = true; +614 run( +615 "== code 0x1\n" +616 " 0f 84 # jmp if ZF to ??\n" +617 ); +618 CHECK_TRACE_CONTENTS( +619 "error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand\n" +620 ); +621 } +622 +623 :(before "End Globals") +624 map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f; +625 :(before "End Init Permitted Operands") +626 //// Class D: just op and disp32 +627 // imm32 imm8 disp32 |disp16 disp8 subop modrm +628 // 0 0 1 |0 0 0 0 +629 put_new(Permitted_operands_0f, "82", 0x10); +630 put_new(Permitted_operands_0f, "83", 0x10); +631 put_new(Permitted_operands_0f, "84", 0x10); +632 put_new(Permitted_operands_0f, "85", 0x10); +633 put_new(Permitted_operands_0f, "86", 0x10); +634 put_new(Permitted_operands_0f, "87", 0x10); +635 put_new(Permitted_operands_0f, "8c", 0x10); +636 put_new(Permitted_operands_0f, "8d", 0x10); +637 put_new(Permitted_operands_0f, "8e", 0x10); +638 put_new(Permitted_operands_0f, "8f", 0x10); +639 +640 //// Class M: using ModR/M byte +641 // imm32 imm8 disp32 |disp16 disp8 subop modrm +642 // 0 0 0 |0 0 0 1 +643 put_new(Permitted_operands_0f, "af", 0x01); +644 +645 :(code) +646 void check_operands_0f(const line& inst, const word& op) { +647 uint8_t expected_bitvector = get(Permitted_operands_0f, op.data); +648 if (HAS(expected_bitvector, MODRM)) +649 check_operands_modrm(inst, op); +650 compare_bitvector_0f(inst, CLEAR(expected_bitvector, MODRM), op); +651 } +652 +653 void compare_bitvector_0f(const line& inst, uint8_t expected, const word& op) { +654 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +655 uint8_t bitvector = compute_expected_operand_bitvector(inst); +656 if (trace_contains_errors()) return; // duplicate operand type +657 if (bitvector == expected) return; // all good with this instruction +658 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { +659 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; +660 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand +661 const string& optype = Operand_type_name.at(i); +662 if ((bitvector & 0x1) > (expected & 0x1)) +663 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": unexpected " << optype << " operand\n" << end(); +664 else +665 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": missing " << optype << " operand\n" << end(); +666 // continue giving all errors for a single instruction +667 } +668 // ignore settings in any unused bits +669 } +670 +671 string maybe_name_0f(const word& op) { +672 if (!is_hex_byte(op)) return ""; +673 if (!contains_key(Name_0f, op.data)) return ""; +674 // strip stuff in parens from the name +675 const string& s = get(Name_0f, op.data); +676 return " ("+s.substr(0, s.find(" ("))+')'; +677 } +678 +679 string tolower(const char* s) { +680 ostringstream out; +681 for (/*nada*/; *s; ++s) +682 out << static_cast<char>(tolower(*s)); +683 return out.str(); +684 } +685 +686 #undef HAS +687 #undef SET +688 #undef CLEAR +689 +690 :(before "End Includes") +691 #include<cctype> diff --git a/html/subx/032check_operand_bounds.cc.html b/html/subx/032check_operand_bounds.cc.html index 613e4051..433f1549 100644 --- a/html/subx/032check_operand_bounds.cc.html +++ b/html/subx/032check_operand_bounds.cc.html @@ -3,8 +3,8 @@ Mu - subx/032check_operand_bounds.cc - - + + @@ -14,14 +14,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.cSpecial { color: #008000; } .LineNr { } .Constant { color: #008787; } -.Delimiter { color: #c000c0; } +.Comment { color: #005faf; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Delimiter { color: #c000c0; } +.cSpecial { color: #008000; } .SalientComment { color: #0000af; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -95,7 +95,7 @@ if ('onhashchange' in window) { 35 trace(3, "transform") << "-- check operand bounds" << end(); 36 for (int i = 0; i < SIZE(code.lines); ++i) { 37 const line& inst = code.lines.at(i); - 38 for (int j = first_operand(inst); j < SIZE(inst.words); ++j) + 38 for (int j = first_operand(inst); j < SIZE(inst.words); ++j) 39 check_operand_bounds(inst.words.at(j)); 40 if (trace_contains_errors()) return; // stop at the first mal-formed instruction 41 } diff --git a/html/subx/034compute_segment_address.cc.html b/html/subx/034compute_segment_address.cc.html index 287938c9..06225394 100644 --- a/html/subx/034compute_segment_address.cc.html +++ b/html/subx/034compute_segment_address.cc.html @@ -3,8 +3,8 @@ Mu - subx/034compute_segment_address.cc - - + + @@ -16,11 +16,11 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } .Constant { color: #008787; } -.Delimiter { color: #c000c0; } +.Comment { color: #005faf; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Delimiter { color: #c000c0; } --> @@ -37,7 +37,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -96,7 +96,7 @@ if ('onhashchange' in window) { 38 // valid address for user space, so assume we're creating a real ELF binary, not just running a test 39 curr.start &= 0xfffff000; // same number of zeros as the p_align used when emitting the ELF binary 40 curr.start |= p_offset; -41 trace(99, "transform") << "segment " << i << " begins at address 0x" << HEXWORD << curr.start << end(); +41 trace(99, "transform") << "segment " << i << " begins at address 0x" << HEXWORD << curr.start << end(); 42 } 43 p_offset += size_of(curr); 44 assert(p_offset < SEGMENT_ALIGNMENT); // for now we get less and less available space in each successive segment diff --git a/html/subx/035labels.cc.html b/html/subx/035labels.cc.html index 6c28ee62..9dfb9067 100644 --- a/html/subx/035labels.cc.html +++ b/html/subx/035labels.cc.html @@ -3,8 +3,8 @@ Mu - subx/035labels.cc - - + + @@ -16,11 +16,11 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } .Constant { color: #008787; } -.Delimiter { color: #c000c0; } +.Comment { color: #005faf; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Delimiter { color: #c000c0; } .cSpecial { color: #008000; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -198,7 +198,7 @@ if ('onhashchange' in window) { 139 for (int i = 0; i < SIZE(code.lines); ++i) { 140 const line& inst = code.lines.at(i); 141 if (Source_lines_file.is_open() && !inst.original.empty() && /*not a label*/ *inst.words.at(0).data.rbegin() != ':') -142 Source_lines_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << inst.original << '\n'; +142 Source_lines_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << inst.original << '\n'; 143 for (int j = 0; j < SIZE(inst.words); ++j) { 144 const word& curr = inst.words.at(j); 145 // hack: if we have any operand metadata left after previous transforms, @@ -230,7 +230,7 @@ if ('onhashchange' in window) { 171 if (j > 0) 172 raise << "'" << to_string(inst) << "': labels can only be the first word in a line.\n" << end(); 173 if (Labels_file.is_open()) -174 Labels_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n'; +174 Labels_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n'; 175 if (contains_key(byte_index, label) && label != "Entry") { 176 raise << "duplicate label '" << label << "'\n" << end(); 177 return; diff --git a/html/subx/036global_variables.cc.html b/html/subx/036global_variables.cc.html index e6672030..ba0ae24d 100644 --- a/html/subx/036global_variables.cc.html +++ b/html/subx/036global_variables.cc.html @@ -3,8 +3,8 @@ Mu - subx/036global_variables.cc - - + + @@ -17,12 +17,12 @@ a { color:inherit; } .CommentedCode { color: #8a8a8a; } .LineNr { } .Constant { color: #008787; } -.SalientComment { color: #0000af; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.SalientComment { color: #0000af; } .cSpecial { color: #008000; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -118,13 +118,13 @@ if ('onhashchange' in window) { 57 if (j > 0) 58 raise << "'" << to_string(inst) << "': global variable names can only be the first word in a line.\n" << end(); 59 if (Labels_file.is_open()) - 60 Labels_file << "0x" << HEXWORD << current_address << ' ' << variable << '\n'; + 60 Labels_file << "0x" << HEXWORD << current_address << ' ' << variable << '\n'; 61 if (contains_key(address, variable)) { 62 raise << "duplicate global '" << variable << "'\n" << end(); 63 return; 64 } 65 put(address, variable, current_address); - 66 trace(99, "transform") << "global variable '" << variable << "' is at address 0x" << HEXWORD << current_address << end(); + 66 trace(99, "transform") << "global variable '" << variable << "' is at address 0x" << HEXWORD << current_address << end(); 67 // no modifying current_address; global variable definitions won't be in the final binary 68 } 69 } @@ -201,7 +201,7 @@ if ('onhashchange' in window) { 140 } 141 continue; 142 } -143 trace(99, "transform") << curr.data << " maps to " << HEXWORD << get(address, curr.data) << end(); +143 trace(99, "transform") << curr.data << " maps to " << HEXWORD << get(address, curr.data) << end(); 144 emit_hex_bytes(new_l, get(address, curr.data), 4); 145 } 146 l.words.swap(new_l.words); diff --git a/html/subx/038---literal_strings.cc.html b/html/subx/038---literal_strings.cc.html index b74c47db..01c4a0ca 100644 --- a/html/subx/038---literal_strings.cc.html +++ b/html/subx/038---literal_strings.cc.html @@ -3,8 +3,8 @@ Mu - subx/038---literal_strings.cc - - + + @@ -16,11 +16,11 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } .Constant { color: #008787; } -.Delimiter { color: #c000c0; } +.Comment { color: #005faf; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Special { color: #d70000; } .Identifier { color: #af5f00; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Delimiter { color: #c000c0; } .cSpecial { color: #008000; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/039debug.cc.html b/html/subx/039debug.cc.html index 60653e04..fa0901d6 100644 --- a/html/subx/039debug.cc.html +++ b/html/subx/039debug.cc.html @@ -3,8 +3,8 @@ Mu - subx/039debug.cc - - + + @@ -14,14 +14,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.Todo { color: #000000; background-color: #ffff00; padding-bottom: 1px; } .LineNr { } .Constant { color: #008787; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } +.Todo { color: #000000; background-color: #ffff00; padding-bottom: 1px; } .SalientComment { color: #0000af; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -91,7 +91,7 @@ if ('onhashchange' in window) { 31 fin >> addr; 32 string line; 33 getline(fin, line); - 34 put(Source_line, addr, hacky_squeeze_out_whitespace(line)); + 34 put(Source_line, addr, hacky_squeeze_out_whitespace(line)); 35 } 36 } 37 @@ -99,17 +99,17 @@ if ('onhashchange' in window) { 39 if (contains_key(Symbol_name, EIP)) 40 trace(Callstack_depth, "run") << "== label " << get(Symbol_name, EIP) << end(); 41 if (contains_key(Source_line, EIP)) - 42 trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << get(Source_line, EIP) << end(); + 42 trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << get(Source_line, EIP) << end(); 43 else 44 // no source line info; do what you can - 45 trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << debug_info(EIP) << end(); + 45 trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << debug_info(EIP) << end(); 46 47 :(code) 48 string debug_info(uint32_t inst_address) { 49 uint8_t op = read_mem_u8(inst_address); 50 if (op != 0xe8) { 51 ostringstream out; - 52 out << HEXBYTE << NUM(op); + 52 out << HEXBYTE << NUM(op); 53 return out.str(); 54 } 55 int32_t offset = read_mem_i32(inst_address+/*skip op*/1); @@ -117,7 +117,7 @@ if ('onhashchange' in window) { 57 if (contains_key(Symbol_name, next_eip)) 58 return "e8/call "+get(Symbol_name, next_eip); 59 ostringstream out; - 60 out << "e8/call 0x" << HEXWORD << next_eip; + 60 out << "e8/call 0x" << HEXWORD << next_eip; 61 return out.str(); 62 } 63 @@ -134,9 +134,9 @@ if ('onhashchange' in window) { 74 :(code) 75 void dump_watch_points() { 76 if (Watch_points.empty()) return; - 77 dbg << "watch points:" << end(); + 77 trace(Callstack_depth, "dbg") << "watch points:" << end(); 78 for (map<string, uint32_t>::iterator p = Watch_points.begin(); p != Watch_points.end(); ++p) - 79 dbg << " " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end(); + 79 trace(Callstack_depth, "dbg") << " " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end(); 80 } 81 82 :(before "End Globals") @@ -147,50 +147,64 @@ if ('onhashchange' in window) { 87 Watch_this_effective_address = get(Symbol_name, EIP); 88 :(after "Found effective_address(addr)") 89 if (!Watch_this_effective_address.empty()) { - 90 dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end(); + 90 dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end(); 91 put(Watch_points, Watch_this_effective_address, addr); 92 } 93 - 94 //: helpers - 95 - 96 :(code) - 97 string hacky_squeeze_out_whitespace(const string& s) { - 98 // strip whitespace at start - 99 string::const_iterator first = s.begin(); -100 while (first != s.end() && isspace(*first)) -101 ++first; -102 if (first == s.end()) return ""; -103 -104 // strip whitespace at end -105 string::const_iterator last = --s.end(); -106 while (last != s.begin() && isspace(*last)) -107 --last; -108 ++last; + 94 //: Special label that dumps regions of memory. + 95 //: Not a general mechanism; by the time you get here you're willing to hack + 96 //: on the emulator. + 97 :(after "Run One Instruction") + 98 if (contains_key(Symbol_name, EIP) && get(Symbol_name, EIP) == "$dump-stream-at-EAX") + 99 dump_stream_at(Reg[EAX].u); +100 :(code) +101 void dump_stream_at(uint32_t stream_start) { +102 int32_t stream_length = read_mem_i32(stream_start + 8); +103 dbg << "stream length: " << std::dec << stream_length << end(); +104 for (int i = 0; i < stream_length + 12; ++i) +105 dbg << "0x" << HEXWORD << (stream_start+i) << ": " << HEXBYTE << NUM(read_mem_u8(stream_start+i)) << end(); +106 } +107 +108 //: helpers 109 -110 // replace runs of spaces/dots with single space until comment or string -111 // TODO: -112 // leave alone dots not surrounded by whitespace -113 // leave alone '#' within word -114 // leave alone '"' within word -115 // squeeze spaces after end of string -116 ostringstream out; -117 bool previous_was_space = false; -118 bool in_comment_or_string = false; -119 for (string::const_iterator curr = first; curr != last; ++curr) { -120 if (in_comment_or_string) -121 out << *curr; -122 else if (isspace(*curr) || *curr == '.') -123 previous_was_space = true; -124 else { -125 if (previous_was_space) -126 out << ' '; -127 out << *curr; -128 previous_was_space = false; -129 if (*curr == '#' || *curr == '"') in_comment_or_string = true; -130 } -131 } -132 return out.str(); -133 } +110 :(code) +111 string hacky_squeeze_out_whitespace(const string& s) { +112 // strip whitespace at start +113 string::const_iterator first = s.begin(); +114 while (first != s.end() && isspace(*first)) +115 ++first; +116 if (first == s.end()) return ""; +117 +118 // strip whitespace at end +119 string::const_iterator last = --s.end(); +120 while (last != s.begin() && isspace(*last)) +121 --last; +122 ++last; +123 +124 // replace runs of spaces/dots with single space until comment or string +125 // TODO: +126 // leave alone dots not surrounded by whitespace +127 // leave alone '#' within word +128 // leave alone '"' within word +129 // squeeze spaces after end of string +130 ostringstream out; +131 bool previous_was_space = false; +132 bool in_comment_or_string = false; +133 for (string::const_iterator curr = first; curr != last; ++curr) { +134 if (in_comment_or_string) +135 out << *curr; +136 else if (isspace(*curr) || *curr == '.') +137 previous_was_space = true; +138 else { +139 if (previous_was_space) +140 out << ' '; +141 out << *curr; +142 previous_was_space = false; +143 if (*curr == '#' || *curr == '"') in_comment_or_string = true; +144 } +145 } +146 return out.str(); +147 } diff --git a/html/subx/040---tests.cc.html b/html/subx/040---tests.cc.html index 48e78107..9e19ae62 100644 --- a/html/subx/040---tests.cc.html +++ b/html/subx/040---tests.cc.html @@ -3,8 +3,8 @@ Mu - subx/040---tests.cc - - + + @@ -16,11 +16,11 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } .Constant { color: #008787; } +.Comment { color: #005faf; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Comment { color: #005faf; } .cSpecial { color: #008000; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/049memory_layout.subx.html b/html/subx/049memory_layout.subx.html index db21c5c7..d3c5ad73 100644 --- a/html/subx/049memory_layout.subx.html +++ b/html/subx/049memory_layout.subx.html @@ -3,8 +3,8 @@ Mu - subx/049memory_layout.subx - - + + @@ -32,7 +32,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/050_write.subx.html b/html/subx/050_write.subx.html index e5d0f459..5205e615 100644 --- a/html/subx/050_write.subx.html +++ b/html/subx/050_write.subx.html @@ -3,8 +3,8 @@ Mu - subx/050_write.subx - - + + @@ -15,13 +15,13 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Constant { color: #008787; } .subxMinorFunction { color: #875f5f; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxS2Comment { color: #8a8a8a; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/051test.subx.html b/html/subx/051test.subx.html index 4b854b84..d4b6665e 100644 --- a/html/subx/051test.subx.html +++ b/html/subx/051test.subx.html @@ -3,8 +3,8 @@ Mu - subx/051test.subx - - + + @@ -15,13 +15,13 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } .Constant { color: #008787; } .subxFunction { color: #af5f00; text-decoration: underline; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxS2Comment { color: #8a8a8a; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/052kernel-string-equal.subx.html b/html/subx/052kernel-string-equal.subx.html index f26b805e..e7715bbd 100644 --- a/html/subx/052kernel-string-equal.subx.html +++ b/html/subx/052kernel-string-equal.subx.html @@ -3,8 +3,8 @@ Mu - subx/052kernel-string-equal.subx - - + + @@ -14,17 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.subxMinorFunction { color: #875f5f; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxTest { color: #5f8700; } --> @@ -41,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/053new-segment.subx.html b/html/subx/053new-segment.subx.html index 91bccce0..cab22dc6 100644 --- a/html/subx/053new-segment.subx.html +++ b/html/subx/053new-segment.subx.html @@ -3,8 +3,8 @@ Mu - subx/053new-segment.subx - - + + @@ -15,14 +15,14 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxFunction { color: #af5f00; text-decoration: underline; } .Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/054string-equal.subx.html b/html/subx/054string-equal.subx.html index 4eb247f4..2d315db5 100644 --- a/html/subx/054string-equal.subx.html +++ b/html/subx/054string-equal.subx.html @@ -3,8 +3,8 @@ Mu - subx/054string-equal.subx - - + + @@ -14,17 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.subxS2Comment { color: #8a8a8a; } .CommentedCode { color: #8a8a8a; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxS2Comment { color: #8a8a8a; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxTest { color: #5f8700; } --> @@ -41,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -77,171 +77,219 @@ if ('onhashchange' in window) { 15 16 string-equal?: # s : (address string), benchmark : (address string) -> EAX : boolean 17 # pseudocode: - 18 # lens = s->length - 19 # if (lens != benchmark->length) return false - 20 # i = 0 - 21 # currs = s->data - 22 # currb = benchmark->data - 23 # while i < s->length - 24 # c1 = *currs - 25 # c2 = *currb - 26 # if (c1 != c2) return false - 27 # ++i, ++currs, ++currb - 28 # return true - 29 # - 30 # registers: - 31 # i: ECX - 32 # lens: EDX - 33 # currs: ESI - 34 # currb: EDI - 35 # c1: EAX - 36 # c2: EBX - 37 # - 38 # . prolog - 39 55/push-EBP - 40 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 41 # . save registers - 42 51/push-ECX - 43 52/push-EDX - 44 53/push-EBX - 45 56/push-ESI - 46 57/push-EDI - 47 # ESI = s - 48 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - 49 # EDI = benchmark - 50 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - 51 # lens/EDX = s->length - 52 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 53 $string-equal?:lengths: - 54 # if (lens != benchmark->length) return false - 55 39/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare *EDI and EDX - 56 75/jump-if-not-equal $string-equal?:false/disp8 - 57 # currs/ESI = s->data - 58 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - 59 # currb/EDI = benchmark->data - 60 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - 61 # i/ECX = c1/EAX = c2/EBX = 0 - 62 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - 63 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 64 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 65 $string-equal?:loop: - 66 # if (i >= lens) return true - 67 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 68 7d/jump-if-greater-or-equal $string-equal?:true/disp8 - 69 # c1 = *currs - 70 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - 71 # c2 = *currb - 72 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at *EDI to BL - 73 # if (c1 != c2) return false - 74 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75 75/jump-if-not-equal $string-equal?:false/disp8 - 76 # ++i - 77 41/increment-ECX - 78 # ++currs - 79 46/increment-ESI - 80 # ++currb - 81 47/increment-EDI - 82 eb/jump $string-equal?:loop/disp8 - 83 $string-equal?:true: - 84 b8/copy-to-EAX 1/imm32 - 85 eb/jump $string-equal?:end/disp8 - 86 $string-equal?:false: - 87 b8/copy-to-EAX 0/imm32 - 88 $string-equal?:end: - 89 # . restore registers - 90 5f/pop-to-EDI - 91 5e/pop-to-ESI - 92 5b/pop-to-EBX - 93 5a/pop-to-EDX - 94 59/pop-to-ECX - 95 # . epilog - 96 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 97 5d/pop-to-EBP - 98 c3/return - 99 -100 # - tests -101 -102 test-compare-empty-with-empty-string: -103 # EAX = string-equal?("", "") -104 # . . push args -105 68/push ""/imm32 -106 68/push ""/imm32 -107 # . . call -108 e8/call string-equal?/disp32 -109 # . . discard args -110 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -111 # check-ints-equal(EAX, 1, msg) -112 # . . push args -113 68/push "F - test-compare-empty-with-empty-string"/imm32 -114 68/push 1/imm32/true -115 50/push-EAX -116 # . . call -117 e8/call check-ints-equal/disp32 -118 # . . discard args -119 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -120 c3/return -121 -122 test-compare-empty-with-non-empty-string: # also checks length-mismatch code path -123 # EAX = string-equal?("", "Abc") -124 # . . push args -125 68/push "Abc"/imm32 -126 68/push ""/imm32 -127 # . . call -128 e8/call string-equal?/disp32 -129 # . . discard args -130 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -131 # check-ints-equal(EAX, 0, msg) -132 # . . push args -133 68/push "F - test-compare-empty-with-non-empty-string"/imm32 -134 68/push 0/imm32/false -135 50/push-EAX -136 # . . call -137 e8/call check-ints-equal/disp32 -138 # . . discard args -139 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -140 c3/return -141 -142 test-compare-equal-strings: -143 # EAX = string-equal?("Abc", "Abc") -144 # . . push args -145 68/push "Abc"/imm32 -146 68/push "Abc"/imm32 -147 # . . call -148 e8/call string-equal?/disp32 -149 # . . discard args -150 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -151 # check-ints-equal(EAX, 1, msg) -152 # . . push args -153 68/push "F - test-compare-equal-strings"/imm32 -154 68/push 1/imm32/true -155 50/push-EAX -156 # . . call -157 e8/call check-ints-equal/disp32 -158 # . . discard args -159 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -160 c3/return -161 -162 test-compare-inequal-strings-equal-lengths: -163 # EAX = string-equal?("Abc", "Adc") -164 # . . push args -165 68/push "Adc"/imm32 -166 68/push "Abc"/imm32 -167 # . . call -168 e8/call string-equal?/disp32 -169 # . . discard args -170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -171 # check-ints-equal(EAX, 0, msg) -172 # . . push args -173 68/push "F - test-compare-inequal-strings-equal-lengths"/imm32 -174 68/push 0/imm32/false -175 50/push-EAX -176 # . . call -177 e8/call check-ints-equal/disp32 -178 # . . discard args -179 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -180 c3/return -181 -182 # . . vim:nowrap:textwidth=0 + 18 # if (s->length != benchmark->length) return false + 19 # currs = s->data + 20 # currb = benchmark->data + 21 # maxs = s->data + s->length + 22 # while currs < maxs + 23 # c1 = *currs + 24 # c2 = *currb + 25 # if (c1 != c2) return false + 26 # ++currs, ++currb + 27 # return true + 28 # + 29 # registers: + 30 # currs: ESI + 31 # maxs: ECX + 32 # currb: EDI + 33 # c1: EAX + 34 # c2: EBX + 35 # + 36 # . prolog + 37 55/push-EBP + 38 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 39 # . save registers + 40 51/push-ECX + 41 52/push-EDX + 42 56/push-ESI + 43 57/push-EDI + 44 # ESI = s + 45 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 46 # EDI = benchmark + 47 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + 48 # ECX = s->length + 49 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX + 50 $string-equal?:lengths: + 51 # if (ECX != benchmark->length) return false + 52 39/compare 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # compare *EDI and ECX + 53 75/jump-if-not-equal $string-equal?:false/disp8 + 54 # currs/ESI = s->data + 55 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI + 56 # maxs/ECX = s->data + s->length + 57 01/add 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # add ESI to ECX + 58 # currb/EDI = benchmark->data + 59 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI + 60 # c1/EAX = c2/EDX = 0 + 61 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 62 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX + 63 $string-equal?:loop: + 64 # if (currs >= maxs) return true + 65 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX + 66 73/jump-if-greater-or-equal-unsigned $string-equal?:true/disp8 + 67 # c1 = *currs + 68 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL + 69 # c2 = *currb + 70 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 2/r32/DL . . # copy byte at *EDI to DL + 71 # if (c1 != c2) return false + 72 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX and EDX + 73 75/jump-if-not-equal $string-equal?:false/disp8 + 74 # ++currs + 75 46/increment-ESI + 76 # ++currb + 77 47/increment-EDI + 78 eb/jump $string-equal?:loop/disp8 + 79 $string-equal?:true: + 80 b8/copy-to-EAX 1/imm32 + 81 eb/jump $string-equal?:end/disp8 + 82 $string-equal?:false: + 83 b8/copy-to-EAX 0/imm32 + 84 $string-equal?:end: + 85 # . restore registers + 86 5f/pop-to-EDI + 87 5e/pop-to-ESI + 88 5a/pop-to-EDX + 89 59/pop-to-ECX + 90 # . epilog + 91 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 92 5d/pop-to-EBP + 93 c3/return + 94 + 95 # - tests + 96 + 97 test-compare-empty-with-empty-string: + 98 # EAX = string-equal?("", "") + 99 # . . push args +100 68/push ""/imm32 +101 68/push ""/imm32 +102 # . . call +103 e8/call string-equal?/disp32 +104 # . . discard args +105 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +106 # check-ints-equal(EAX, 1, msg) +107 # . . push args +108 68/push "F - test-compare-empty-with-empty-string"/imm32 +109 68/push 1/imm32/true +110 50/push-EAX +111 # . . call +112 e8/call check-ints-equal/disp32 +113 # . . discard args +114 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +115 c3/return +116 +117 test-compare-empty-with-non-empty-string: # also checks length-mismatch code path +118 # EAX = string-equal?("", "Abc") +119 # . . push args +120 68/push "Abc"/imm32 +121 68/push ""/imm32 +122 # . . call +123 e8/call string-equal?/disp32 +124 # . . discard args +125 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +126 # check-ints-equal(EAX, 0, msg) +127 # . . push args +128 68/push "F - test-compare-empty-with-non-empty-string"/imm32 +129 68/push 0/imm32/false +130 50/push-EAX +131 # . . call +132 e8/call check-ints-equal/disp32 +133 # . . discard args +134 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +135 c3/return +136 +137 test-compare-equal-strings: +138 # EAX = string-equal?("Abc", "Abc") +139 # . . push args +140 68/push "Abc"/imm32 +141 68/push "Abc"/imm32 +142 # . . call +143 e8/call string-equal?/disp32 +144 # . . discard args +145 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +146 # check-ints-equal(EAX, 1, msg) +147 # . . push args +148 68/push "F - test-compare-equal-strings"/imm32 +149 68/push 1/imm32/true +150 50/push-EAX +151 # . . call +152 e8/call check-ints-equal/disp32 +153 # . . discard args +154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +155 c3/return +156 +157 test-compare-inequal-strings-equal-lengths: +158 # EAX = string-equal?("Abc", "Adc") +159 # . . push args +160 68/push "Adc"/imm32 +161 68/push "Abc"/imm32 +162 # . . call +163 e8/call string-equal?/disp32 +164 # . . discard args +165 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +166 # check-ints-equal(EAX, 0, msg) +167 # . . push args +168 68/push "F - test-compare-inequal-strings-equal-lengths"/imm32 +169 68/push 0/imm32/false +170 50/push-EAX +171 # . . call +172 e8/call check-ints-equal/disp32 +173 # . . discard args +174 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +175 c3/return +176 +177 # helper for later tests +178 check-string-equal: # s : (address string), expected : (address string), msg : (address string) +179 # . prolog +180 55/push-EBP +181 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +182 # . save registers +183 50/push-EAX +184 # EAX = string-equal?(s, expected) +185 # . . push args +186 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +187 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +188 # . . call +189 e8/call string-equal?/disp32 +190 # . . discard args +191 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +192 # check-ints-equal(EAX, 1, msg) +193 # . . push args +194 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +195 68/push 1/imm32 +196 50/push-EAX +197 # . . call +198 e8/call check-ints-equal/disp32 +199 # . . discard args +200 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +201 $check-string-equal:end: +202 # . restore registers +203 58/pop-to-EAX +204 # . epilog +205 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +206 5d/pop-to-EBP +207 c3/return +208 +209 # test the helper +210 test-check-string-equal: +211 # check-string-equal?("Abc", "Abc") +212 # . . push args +213 68/push "Abc"/imm32 +214 68/push "Abc"/imm32 +215 # . . call +216 e8/call check-string-equal/disp32 +217 # . . discard args +218 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +219 # check-ints-equal(EAX, 1, msg) +220 # . . push args +221 68/push "F - test-check-string-equal"/imm32 +222 68/push 0/imm32/false +223 50/push-EAX +224 # . . call +225 e8/call check-ints-equal/disp32 +226 # . . discard args +227 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +228 c3/return +229 +230 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/055stream.subx.html b/html/subx/055stream.subx.html index e339250c..0ab5f47f 100644 --- a/html/subx/055stream.subx.html +++ b/html/subx/055stream.subx.html @@ -3,8 +3,8 @@ Mu - subx/055stream.subx - - + + @@ -15,11 +15,11 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } .subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.LineNr { } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } .subxS2Comment { color: #8a8a8a; } --> @@ -37,7 +37,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -95,7 +95,7 @@ if ('onhashchange' in window) { 37 $clear-stream:loop: 38 # if (EAX >= ECX) break 39 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX -40 7d/jump-if-greater-or-equal $clear-stream:end/disp8 +40 73/jump-if-greater-or-equal-unsigned $clear-stream:end/disp8 41 # *EAX = 0 42 c6 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm8 # copy byte to *EAX 43 # ++EAX diff --git a/html/subx/056trace.subx.html b/html/subx/056trace.subx.html index 8502bbd3..4feb38ed 100644 --- a/html/subx/056trace.subx.html +++ b/html/subx/056trace.subx.html @@ -3,8 +3,8 @@ Mu - subx/056trace.subx - - + + @@ -14,17 +14,18 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.CommentedCode { color: #8a8a8a; } .subxTest { color: #5f8700; } -.subxMinorFunction { color: #875f5f; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.Constant { color: #008787; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } .subxH1Comment { color: #005faf; text-decoration: underline; } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -41,7 +42,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -60,371 +61,1015 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/056trace.subx
-  1 # primitives for emitting traces to a 'trace' stream, and for tests to make assertions on its contents
-  2 #
-  3 # A trace stream looks like a regular stream:
-  4 #   write : int  # index at which writes go
-  5 #   read : int  # index that we've read until
-  6 #   data : (array byte)  # prefixed by length as usual
-  7 # In a real trace the data will be in a special segment set aside for the purpose.
-  8 #
-  9 # primitives for operating on traces:
- 10 #   - initialize-trace-stream (update global variable)
- 11 #   - trace: stream, string
- 12 #   - die: stream (exit(1) if using real trace)
- 13 #   - check-trace-contains: stream, string/line, string/message (scans only from stream's read pointer, prints message to stderr on failure, updates stream's read pointer)
- 14 #   - scan-to-next-line: stream (advance read pointer past next newline)
- 15 #
- 16 # Traces are very fundamental, so many of the helpers we create here won't be
- 17 # used elsewhere; we'll switch to more bounds-checked variants. But here we get
- 18 # bounds-checking for free; we allocate a completely disjoint segment for trace
- 19 # data, and overflowing it will generate a page fault.
- 20 
- 21 == data
- 22 
- 23 # We'll save the address of the trace segment here.
- 24 Trace-stream:
- 25     0/imm32
- 26 
- 27 # Fake trace-stream for tests.
- 28 # Also illustrates the layout of the real trace-stream (segment).
- 29 _test-trace-stream:
- 30     # current write index
- 31     0/imm32
- 32     # current read index
- 33     0/imm32
- 34     # length
- 35     8/imm32
- 36     # data
- 37     00 00 00 00 00 00 00 00  # 8 bytes
- 38 
- 39 == code
- 40 #   instruction                     effective address                                                   register    displacement    immediate
- 41 # . op          subop               mod             rm32          base        index         scale       r32
- 42 # . 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
- 43 
- 44 # Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream.
- 45 # The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
- 46 initialize-trace-stream:
- 47     # EAX = new-segment(0x1000)
- 48     # . . push args
- 49     68/push  0x1000/imm32/N
- 50     # . . call
- 51     e8/call  new-segment/disp32
- 52     # . . discard args
- 53     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 54     # copy EAX to *Trace-stream
- 55     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
- 56     # Trace-stream->length = 0x1000/N - 12
- 57     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         0xff4/imm32       # copy 0xff4 to *(EAX+8)
- 58     c3/return
- 59 
- 60 # Append a string to the given trace stream.
- 61 # Silently give up if it's already full. Or truncate the string if there isn't enough room.
- 62 trace:  # t : (address trace-stream), line : string
- 63     # . prolog
- 64     55/push-EBP
- 65     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 66     # . save registers
- 67     50/push-EAX
- 68     51/push-ECX
- 69     52/push-EDX
- 70     53/push-EBX
- 71     56/push-ESI
- 72     57/push-EDI
- 73     # EDI = t
- 74     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
- 75     # ESI = line
- 76     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
- 77     # ECX = t->write
- 78     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
- 79     # EDX = t->length
- 80     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(EDI+8) to EDX
- 81     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
- 82     # . . push line
- 83     56/push-ESI
- 84     # . . push &t->data[t->length]
- 85     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
- 86     53/push-EBX
- 87     # . . push &t->data[t->write]
- 88     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
- 89     53/push-EBX
- 90     # . . call
- 91     e8/call  _append-3/disp32
- 92     # . . discard args
- 93     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 94     # if (EAX == 0) return
- 95     3d/compare-EAX-and  0/imm32
- 96     74/jump-if-equal  $trace:end/disp8
- 97     # t->write += EAX
- 98     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
- 99     # refresh ECX = t->write
-100     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
-101     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
-102     # . . push line
-103     68/push  Newline/imm32
-104     # . . push &t->data[t->length]
-105     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
-106     53/push-EBX
-107     # . . push &t->data[t->write]
-108     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
-109     53/push-EBX
-110     # . . call
-111     e8/call  _append-3/disp32
-112     # . . discard args
-113     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-114     # t->write += EAX
-115     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
-116 $trace:end:
-117     # . restore registers
-118     5f/pop-to-EDI
-119     5e/pop-to-ESI
-120     5b/pop-to-EBX
-121     5a/pop-to-EDX
-122     59/pop-to-ECX
-123     58/pop-to-EAX
-124     # . epilog
-125     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-126     5d/pop-to-EBP
-127     c3/return
-128 
-129 clear-trace-stream:  # t : (address trace-stream)
-130     # . prolog
-131     55/push-EBP
-132     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-133     # . save registers
-134     50/push-EAX
-135     51/push-ECX
-136     # EAX = t
-137     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-138     # ECX = t->length
-139     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EAX+8) to ECX
-140     # ECX = &t->data[t->length]
-141     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EAX+ECX+12 to ECX
-142     # t->write = 0
-143     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-144     # t->read = 0
-145     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
-146     # EAX = t->data
-147     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xc/imm32         # add to EAX
-148 $clear-trace-stream:loop:
-149     # if (EAX >= ECX) break
-150     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
-151     7d/jump-if-greater-or-equal  $clear-trace-stream:end/disp8
-152     # *EAX = 0
-153     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-154     # EAX += 4
-155     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm32           # add to EAX
-156     eb/jump  $clear-trace-stream:loop/disp8
-157 $clear-trace-stream:end:
-158     # . restore registers
-159     59/pop-to-ECX
-160     58/pop-to-EAX
-161     # . epilog
-162     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-163     5d/pop-to-EBP
-164     c3/return
-165 
-166 # - tests
-167 
-168 test-trace-single:
-169     # clear-trace-stream(_test-trace-stream)
-170     # . . push args
-171     68/push  _test-trace-stream/imm32
-172     # . . call
-173     e8/call  clear-trace-stream/disp32
-174     # . . discard args
-175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-176     # trace(_test-trace-stream, "Ab")
-177     # . . push args
-178     68/push  "Ab"/imm32
-179     68/push  _test-trace-stream/imm32
-180     # . . call
-181     e8/call  trace/disp32
-182     # . . discard args
-183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-184     # check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
-185     # . . push args
-186     68/push  "F - test-trace-single"/imm32
-187     68/push  0x0a6241/imm32/Ab-newline
-188     # . . push *_test-trace-stream->data
-189     b8/copy-to-EAX  _test-trace-stream/imm32
-190     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-191     # . . call
-192     e8/call  check-ints-equal/disp32
-193     # . . discard args
-194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-195     # end
-196     c3/return
-197 
-198 test-trace-appends:
-199     # clear-trace-stream(_test-trace-stream)
-200     # . . push args
-201     68/push  _test-trace-stream/imm32
-202     # . . call
-203     e8/call  clear-trace-stream/disp32
-204     # . . discard args
-205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-206     # trace(_test-trace-stream, "C")
-207     # . . push args
-208     68/push  "C"/imm32
-209     68/push  _test-trace-stream/imm32
-210     # . . call
-211     e8/call  trace/disp32
-212     # . . discard args
-213     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-214     # trace(_test-trace-stream, "D")
-215     # . . push args
-216     68/push  "D"/imm32
-217     68/push  _test-trace-stream/imm32
-218     # . . call
-219     e8/call  trace/disp32
-220     # . . discard args
-221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-222     # check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
-223     # . . push args
-224     68/push  "F - test-trace-appends"/imm32
-225     68/push  0x0a440a43/imm32/C-newline-D-newline
-226     # . . push *_test-trace-stream->data
-227     b8/copy-to-EAX  _test-trace-stream/imm32
-228     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-229     # . . call
-230     e8/call  check-ints-equal/disp32
-231     # . . discard args
-232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-233     # end
-234     c3/return
-235 
-236 test-trace-empty-line:
-237     # clear-trace-stream(_test-trace-stream)
-238     # . . push args
-239     68/push  _test-trace-stream/imm32
-240     # . . call
-241     e8/call  clear-trace-stream/disp32
-242     # . . discard args
-243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-244     # trace(_test-trace-stream, "")
-245     # . . push args
-246     68/push  ""/imm32
-247     68/push  _test-trace-stream/imm32
-248     # . . call
-249     e8/call  trace/disp32
-250     # . . discard args
-251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-252     # check-ints-equal(*_test-trace-stream->data, 0, msg)
-253     # . . push args
-254     68/push  "F - test-trace-empty-line"/imm32
-255     68/push  0/imm32
-256     # . . push *_test-trace-stream->data
-257     b8/copy-to-EAX  _test-trace-stream/imm32
-258     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-259     # . . call
-260     e8/call  check-ints-equal/disp32
-261     # . . discard args
-262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-263     # end
-264     c3/return
-265 
-266 # - helpers
-267 
-268 # 3-argument variant of _append
-269 _append-3:  # out : address, outend : address, s : (array byte) -> num_bytes_appended/EAX
-270     # . prolog
-271     55/push-EBP
-272     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-273     # . save registers
-274     51/push-ECX
-275     # EAX = _append-4(out, outend, &s->data[0], &s->data[s->length])
-276     # . . push &s->data[s->length]
-277     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
-278     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-279     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-280     51/push-ECX
-281     # . . push &s->data[0]
-282     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
-283     51/push-ECX
-284     # . . push outend
-285     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-286     # . . push out
-287     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-288     # . . call
-289     e8/call  _append-4/disp32
-290     # . . discard args
-291     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-292 $_append-3:end:
-293     # . restore registers
-294     59/pop-to-ECX
-295     # . epilog
-296     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-297     5d/pop-to-EBP
-298     c3/return
-299 
-300 # 4-argument variant of _append
-301 _append-4:  # out : address, outend : address, in : address, inend : address -> num_bytes_appended/EAX
-302     # . prolog
-303     55/push-EBP
-304     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-305     # . save registers
-306     51/push-ECX
-307     52/push-EDX
-308     53/push-EBX
-309     56/push-ESI
-310     57/push-EDI
-311     # EAX/num_bytes_appended = 0
-312     b8/copy-to-EAX  0/imm32
-313     # EDI = out
-314     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
-315     # EDX = outend
-316     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
-317     # ESI = in
-318     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0x10/disp8      .                 # copy *(EBP+16) to ESI
-319     # ECX = inend
-320     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
-321 $_append-4:loop:
-322     # if (in >= inend) break
-323     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI with ECX
-324     7d/jump-if-greater-or-equal  $_append-4:end/disp8
-325     # if (out >= outend) abort  # just to catch test failures fast
-326     39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDI with EDX
-327     7d/jump-if-greater-or-equal  $_append-4:abort/disp8
-328     # *out = *in
-329     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/BL    .               .                 # copy byte at *ESI to BL
-330     88/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *EDI
-331     # ++num_bytes_appended
-332     40/increment-EAX
-333     # ++in
-334     46/increment-ESI
-335     # ++out
-336     47/increment-EDI
-337     eb/jump  $_append-4:loop/disp8
-338 $_append-4:end:
-339     # . restore registers
-340     5f/pop-to-EDI
-341     5e/pop-to-ESI
-342     5b/pop-to-EBX
-343     5a/pop-to-EDX
-344     59/pop-to-ECX
-345     # . epilog
-346     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-347     5d/pop-to-EBP
-348     c3/return
-349 
-350 $_append-4:abort:
-351     # . _write(2/stderr, error)
-352     # . . push args
-353     68/push  "stream overflow\n"/imm32
-354     68/push  2/imm32/stderr
-355     # . . call
-356     e8/call  _write/disp32
-357     # . . discard args
-358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-359     # . syscall(exit, 1)
-360     bb/copy-to-EBX  1/imm32
-361     b8/copy-to-EAX  1/imm32/exit
-362     cd/syscall  0x80/imm8
-363     # never gets here
-364 
-365 # . . vim:nowrap:textwidth=0
+   1 # primitives for emitting traces to a 'trace' stream, and for tests to make assertions on its contents
+   2 #
+   3 # A trace stream looks like a regular stream:
+   4 #   write : int  # index at which writes go
+   5 #   read : int  # index that we've read until
+   6 #   data : (array byte)  # prefixed by length as usual
+   7 # Usually the trace stream will be in a separate segment set aside for the purpose.
+   8 #
+   9 # primitives for operating on traces (arguments in quotes):
+  10 #   - initialize-trace-stream: populates Trace-stream with a new segment of the given 'size'
+  11 #   - trace: adds a 'line' to Trace-stream
+  12 #   - check-trace-contains: scans from Trace-stream's start for a matching 'line', prints a 'message' to stderr on failure
+  13 #   - check-trace-scans-to: scans from Trace-stream's read pointer for a matching 'line', prints a 'message' to stderr on failure
+  14 
+  15 == data
+  16 
+  17 # We'll save the address of the trace segment here.
+  18 Trace-stream:
+  19     0/imm32
+  20 
+  21 Trace-segment:
+  22     0/imm32/curr
+  23     0/imm32/limit
+  24 
+  25 # Fake trace-stream for tests.
+  26 # Also illustrates the layout of the real trace-stream (segment).
+  27 _test-trace-stream:
+  28     # current write index
+  29     0/imm32
+  30     # current read index
+  31     0/imm32
+  32     # length
+  33     8/imm32
+  34     # data
+  35     00 00 00 00 00 00 00 00  # 8 bytes
+  36 
+  37 == code
+  38 #   instruction                     effective address                                                   register    displacement    immediate
+  39 # . op          subop               mod             rm32          base        index         scale       r32
+  40 # . 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
+  41 
+  42 # Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream.
+  43 # The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
+  44 initialize-trace-stream:  # n : int -> <void>
+  45     # . prolog
+  46     55/push-EBP
+  47     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  48     # . save registers
+  49     50/push-EAX
+  50     51/push-ECX
+  51     # ECX = n
+  52     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+  53     # Trace-segment = new-segment(n)
+  54     # . . push args
+  55     68/push  Trace-segment/imm32
+  56     51/push-ECX
+  57     # . . call
+  58     e8/call  new-segment/disp32
+  59     # . . discard args
+  60     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  61     # copy Trace-segment->curr to *Trace-stream
+  62     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-segment/disp32              # copy *Trace-segment to EAX
+  63     # watch point to catch Trace-stream leaks
+  64 #? $watch-1:
+  65     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+  66     # Trace-stream->length = n - 12
+  67     # . ECX -= 12
+  68     81          5/subop/subtract    3/mod/direct    1/rm32/ECX    .           .             .           .           .               0xc/imm32         # subtract from ECX
+  69     # . Trace-stream->length = ECX
+  70     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy ECX to *(EAX+8)
+  71 $initialize-trace-stream:end:
+  72     # . restore registers
+  73     59/pop-to-ECX
+  74     58/pop-to-EAX
+  75     # . epilog
+  76     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+  77     5d/pop-to-EBP
+  78     c3/return
+  79 
+  80 # Append a string to the given trace stream.
+  81 # Silently give up if it's already full. Or truncate the string if there isn't enough room.
+  82 trace:  # line : (address string)
+  83     # . prolog
+  84     55/push-EBP
+  85     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  86     # . save registers
+  87     50/push-EAX
+  88     51/push-ECX
+  89     52/push-EDX
+  90     53/push-EBX
+  91     56/push-ESI
+  92     57/push-EDI
+  93     # EDI = *Trace-stream
+  94     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           7/r32/EDI   Trace-stream/disp32               # copy *Trace-stream to EDI
+  95     # ESI = line
+  96     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+  97     # ECX = t->write
+  98     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+  99     # EDX = t->length
+ 100     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(EDI+8) to EDX
+ 101     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
+ 102     # . . push line
+ 103     56/push-ESI
+ 104     # . . push &t->data[t->length]
+ 105     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
+ 106     53/push-EBX
+ 107     # . . push &t->data[t->write]
+ 108     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
+ 109     53/push-EBX
+ 110     # . . call
+ 111     e8/call  _append-3/disp32
+ 112     # . . discard args
+ 113     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 114     # if (EAX == 0) return
+ 115     3d/compare-EAX-and  0/imm32
+ 116     74/jump-if-equal  $trace:end/disp8
+ 117     # t->write += EAX
+ 118     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+ 119     # refresh ECX = t->write
+ 120     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+ 121     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
+ 122     # . . push line
+ 123     68/push  Newline/imm32
+ 124     # . . push &t->data[t->length]
+ 125     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
+ 126     53/push-EBX
+ 127     # . . push &t->data[t->write]
+ 128     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
+ 129     53/push-EBX
+ 130     # . . call
+ 131     e8/call  _append-3/disp32
+ 132     # . . discard args
+ 133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 134     # t->write += EAX
+ 135     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+ 136 $trace:end:
+ 137     # . restore registers
+ 138     5f/pop-to-EDI
+ 139     5e/pop-to-ESI
+ 140     5b/pop-to-EBX
+ 141     5a/pop-to-EDX
+ 142     59/pop-to-ECX
+ 143     58/pop-to-EAX
+ 144     # . epilog
+ 145     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 146     5d/pop-to-EBP
+ 147     c3/return
+ 148 
+ 149 test-trace-single:
+ 150     # push *Trace-stream
+ 151     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 152     # *Trace-stream = _test-trace-stream
+ 153     b8/copy-to-EAX  _test-trace-stream/imm32
+ 154     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 155     # clear-trace-stream()
+ 156     e8/call  clear-trace-stream/disp32
+ 157     # trace("Ab")
+ 158     # . . push args
+ 159     68/push  "Ab"/imm32
+ 160     # . . call
+ 161     e8/call  trace/disp32
+ 162     # . . discard args
+ 163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 164     # check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
+ 165     # . . push args
+ 166     68/push  "F - test-trace-single"/imm32
+ 167     68/push  0x0a6241/imm32/Ab-newline
+ 168     # . . push *_test-trace-stream->data
+ 169     b8/copy-to-EAX  _test-trace-stream/imm32
+ 170     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+ 171     # . . call
+ 172     e8/call  check-ints-equal/disp32
+ 173     # . . discard args
+ 174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 175     # pop into *Trace-stream
+ 176     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+ 177     # end
+ 178     c3/return
+ 179 
+ 180 test-trace-appends:
+ 181     # push *Trace-stream
+ 182     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 183     # *Trace-stream = _test-trace-stream
+ 184     b8/copy-to-EAX  _test-trace-stream/imm32
+ 185     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 186     # clear-trace-stream()
+ 187     e8/call  clear-trace-stream/disp32
+ 188     # trace("C")
+ 189     # . . push args
+ 190     68/push  "C"/imm32
+ 191     # . . call
+ 192     e8/call  trace/disp32
+ 193     # . . discard args
+ 194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 195     # trace("D")
+ 196     # . . push args
+ 197     68/push  "D"/imm32
+ 198     # . . call
+ 199     e8/call  trace/disp32
+ 200     # . . discard args
+ 201     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 202     # check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
+ 203     # . . push args
+ 204     68/push  "F - test-trace-appends"/imm32
+ 205     68/push  0x0a440a43/imm32/C-newline-D-newline
+ 206     # . . push *_test-trace-stream->data
+ 207     b8/copy-to-EAX  _test-trace-stream/imm32
+ 208     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+ 209     # . . call
+ 210     e8/call  check-ints-equal/disp32
+ 211     # . . discard args
+ 212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 213     # pop into *Trace-stream
+ 214     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+ 215     # end
+ 216     c3/return
+ 217 
+ 218 test-trace-empty-line:
+ 219     # push *Trace-stream
+ 220     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 221     # *Trace-stream = _test-trace-stream
+ 222     b8/copy-to-EAX  _test-trace-stream/imm32
+ 223     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 224     # clear-trace-stream()
+ 225     e8/call  clear-trace-stream/disp32
+ 226     # trace("")
+ 227     # . . push args
+ 228     68/push  ""/imm32
+ 229     # . . call
+ 230     e8/call  trace/disp32
+ 231     # . . discard args
+ 232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 233     # check-ints-equal(*_test-trace-stream->data, 0, msg)
+ 234     # . . push args
+ 235     68/push  "F - test-trace-empty-line"/imm32
+ 236     68/push  0/imm32
+ 237     # . . push *_test-trace-stream->data
+ 238     b8/copy-to-EAX  _test-trace-stream/imm32
+ 239     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+ 240     # . . call
+ 241     e8/call  check-ints-equal/disp32
+ 242     # . . discard args
+ 243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 244     # pop into *Trace-stream
+ 245     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+ 246     # end
+ 247     c3/return
+ 248 
+ 249 check-trace-contains:  # line : (address string), msg : (address string)
+ 250     # . prolog
+ 251     55/push-EBP
+ 252     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 253     # rewind-stream(*Trace-stream)
+ 254     # . . push args
+ 255     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 256     # . . call
+ 257     e8/call  rewind-stream/disp32
+ 258     # . . discard args
+ 259     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 260     # check-trace-scans-to(line, msg)
+ 261     # . . push args
+ 262     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 263     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 264     # . . call
+ 265     e8/call  check-trace-scans-to/disp32
+ 266     # . . discard args
+ 267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 268 $check-trace-contains:end:
+ 269     # . epilog
+ 270     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 271     5d/pop-to-EBP
+ 272     c3/return
+ 273 
+ 274 check-trace-scans-to:  # line : (address string), msg : (address string)
+ 275     # . prolog
+ 276     55/push-EBP
+ 277     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 278     # . save registers
+ 279     50/push-EAX
+ 280     # EAX = trace-scan(line)
+ 281     # . . push args
+ 282     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 283     # . . call
+ 284     e8/call  trace-scan/disp32
+ 285     # . . discard args
+ 286     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 287     # check-ints-equal(EAX, 1, msg)
+ 288     # . . push args
+ 289     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 290     68/push  1/imm32
+ 291     50/push-EAX
+ 292     # . . call
+ 293     e8/call check-ints-equal/disp32
+ 294     # . . discard args
+ 295     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 296 $check-trace-scans-to:end:
+ 297     # . restore registers
+ 298     58/pop-to-EAX
+ 299     # . epilog
+ 300     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 301     5d/pop-to-EBP
+ 302     c3/return
+ 303 
+ 304 # Start scanning from Trace-stream->read for 'line'. If found, update Trace-stream->read and return true.
+ 305 trace-scan:  # line : (address string) -> result/EAX : boolean
+ 306     # pseudocode:
+ 307     #   push Trace-stream->read
+ 308     #   while true:
+ 309     #     if Trace-stream->read >= Trace-stream->write
+ 310     #       break
+ 311     #     if next-line-matches?(Trace-stream, line)
+ 312     #       skip-next-line(Trace-stream)
+ 313     #       dump saved copy of Trace-stream->read
+ 314     #       return true
+ 315     #     skip-next-line(Trace-stream)
+ 316     #   pop saved copy of Trace-stream->read
+ 317     #   return false
+ 318     #
+ 319     # . prolog
+ 320     55/push-EBP
+ 321     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 322     # . save registers
+ 323     51/push-ECX
+ 324     56/push-ESI
+ 325     # ESI = *Trace-stream
+ 326     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           6/r32/ESI   Trace-stream/disp32               # copy *Trace-stream to ESI
+ 327     # ECX = Trace-stream->write
+ 328     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX                   .                 # copy *ESI to ECX
+ 329     # push Trace-stream->read
+ 330     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+ 331 $trace-scan:loop:
+ 332     # if (Trace-stream->read >= Trace-stream->write) return false
+ 333     39/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # compare ECX with *(ESI+4)
+ 334     7d/jump-if-greater-or-equal  $trace-scan:false/disp8
+ 335     # EAX = next-line-matches?(Trace-stream, line)
+ 336     # . . push args
+ 337     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 338     56/push-ESI
+ 339     # . . call
+ 340     e8/call  next-line-matches?/disp32
+ 341     # . . discard args
+ 342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 343     # if (EAX == 0) continue
+ 344     3d/compare-EAX-and  0/imm32
+ 345     74/jump-if-equal  $trace-scan:continue/disp8
+ 346 $trace-scan:true:
+ 347     # skip-next-line(Trace-stream)
+ 348     # . . push args
+ 349     56/push-ESI
+ 350     # . . call
+ 351     e8/call  skip-next-line/disp32
+ 352     # . . discard args
+ 353     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 354     # dump saved copy of Trace-stream->read
+ 355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 356     # return true
+ 357     b8/copy-to-EAX  1/imm32/true
+ 358     eb/jump  $trace-scan:end/disp8
+ 359 $trace-scan:continue:
+ 360     # skip-next-line(Trace-stream)
+ 361     # . . push args
+ 362     56/push-ESI
+ 363     # . . call
+ 364     e8/call  skip-next-line/disp32
+ 365     # . . discard args
+ 366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 367     eb/jump  $trace-scan:loop/disp8
+ 368 $trace-scan:false:
+ 369     # restore saved copy of Trace-stream->read
+ 370     8f          0/subop/pop         1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # pop to *(ESI+4)
+ 371     # return false
+ 372     b8/copy-to-EAX  0/imm32/false
+ 373 $trace-scan:end:
+ 374     # . restore registers
+ 375     59/pop-to-ECX
+ 376     # . epilog
+ 377     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 378     5d/pop-to-EBP
+ 379     c3/return
+ 380 
+ 381 test-trace-scan-first:
+ 382     # push *Trace-stream
+ 383     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 384     # setup
+ 385     # . *Trace-stream = _test-trace-stream
+ 386     b8/copy-to-EAX  _test-trace-stream/imm32
+ 387     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 388     # . clear-trace-stream()
+ 389     e8/call  clear-trace-stream/disp32
+ 390     # . trace("Ab")
+ 391     # . . push args
+ 392     68/push  "Ab"/imm32
+ 393     # . . call
+ 394     e8/call  trace/disp32
+ 395     # . . discard args
+ 396     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 397     # EAX = trace-scan("Ab")
+ 398     # . . push args
+ 399     68/push  "Ab"/imm32
+ 400     # . . call
+ 401     e8/call  trace-scan/disp32
+ 402     # . . discard args
+ 403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 404     # check-ints-equal(EAX, 1, msg)
+ 405     # . . push args
+ 406     68/push  "F - test-trace-scan-first"/imm32
+ 407     68/push  1/imm32
+ 408     50/push-EAX
+ 409     # . . call
+ 410     e8/call check-ints-equal/disp32
+ 411     # . . discard args
+ 412     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 413     # pop into *Trace-stream
+ 414     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+ 415     # . end
+ 416     c3/return
+ 417 
+ 418 test-trace-scan-skips-lines-until-found:
+ 419     # push *Trace-stream
+ 420     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 421     # setup
+ 422     # . *Trace-stream = _test-trace-stream
+ 423     b8/copy-to-EAX  _test-trace-stream/imm32
+ 424     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 425     # . clear-trace-stream()
+ 426     e8/call  clear-trace-stream/disp32
+ 427     # . trace("Ab")
+ 428     # . . push args
+ 429     68/push  "Ab"/imm32
+ 430     # . . call
+ 431     e8/call  trace/disp32
+ 432     # . . discard args
+ 433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 434     # . trace("cd")
+ 435     # . . push args
+ 436     68/push  "cd"/imm32
+ 437     # . . call
+ 438     e8/call  trace/disp32
+ 439     # . . discard args
+ 440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 441     # EAX = trace-scan("cd")
+ 442     # . . push args
+ 443     68/push  "cd"/imm32
+ 444     # . . call
+ 445     e8/call  trace-scan/disp32
+ 446     # . . discard args
+ 447     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 448     # check-ints-equal(EAX, 1, msg)
+ 449     # . . push args
+ 450     68/push  "F - test-trace-scan-skips-lines-until-found"/imm32
+ 451     68/push  1/imm32
+ 452     50/push-EAX
+ 453     # . . call
+ 454     e8/call check-ints-equal/disp32
+ 455     # . . discard args
+ 456     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 457     # pop into *Trace-stream
+ 458     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+ 459     # . end
+ 460     c3/return
+ 461 
+ 462 test-trace-second-scan-starts-where-first-left-off:
+ 463     # push *Trace-stream
+ 464     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 465     # setup
+ 466     # . *Trace-stream = _test-trace-stream
+ 467     b8/copy-to-EAX  _test-trace-stream/imm32
+ 468     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 469     # . clear-trace-stream()
+ 470     e8/call  clear-trace-stream/disp32
+ 471     # . trace("Ab")
+ 472     # . . push args
+ 473     68/push  "Ab"/imm32
+ 474     # . . call
+ 475     e8/call  trace/disp32
+ 476     # . . discard args
+ 477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 478     # . EAX = trace-scan("Ab")
+ 479     # . . push args
+ 480     68/push  "Ab"/imm32
+ 481     # . . call
+ 482     e8/call  trace-scan/disp32
+ 483     # . . discard args
+ 484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 485     # second scan fails
+ 486     # . EAX = trace-scan("Ab")
+ 487     # . . push args
+ 488     68/push  "Ab"/imm32
+ 489     # . . call
+ 490     e8/call  trace-scan/disp32
+ 491     # . . discard args
+ 492     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 493     # check-ints-equal(EAX, 0, msg)
+ 494     # . . push args
+ 495     68/push  "F - test-trace-second-scan-starts-where-first-left-off"/imm32
+ 496     68/push  0/imm32
+ 497     50/push-EAX
+ 498     # . . call
+ 499     e8/call check-ints-equal/disp32
+ 500     # . . discard args
+ 501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 502     # pop into *Trace-stream
+ 503     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+ 504     # . end
+ 505     c3/return
+ 506 
+ 507 test-trace-scan-failure-leaves-read-index-untouched:
+ 508     # push *Trace-stream
+ 509     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+ 510     # setup
+ 511     # . *Trace-stream = _test-trace-stream
+ 512     b8/copy-to-EAX  _test-trace-stream/imm32
+ 513     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 514     # . clear-trace-stream()
+ 515     e8/call  clear-trace-stream/disp32
+ 516     # . trace("Ab")
+ 517     # . . push args
+ 518     68/push  "Ab"/imm32
+ 519     # . . call
+ 520     e8/call  trace/disp32
+ 521     # . . discard args
+ 522     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 523     # . check-ints-equal(_test-trace-stream->read, 0, msg)
+ 524     # . . push args
+ 525     68/push  "F - test-trace-second-scan-starts-where-first-left-off/precondition-failure"/imm32
+ 526     68/push  0/imm32
+ 527     b8/copy-to-EAX  _test-trace-stream/imm32
+ 528     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+ 529     # . . call
+ 530     e8/call check-ints-equal/disp32
+ 531     # . . discard args
+ 532     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 533     # perform a failing scan
+ 534     # . EAX = trace-scan("Ax")
+ 535     # . . push args
+ 536     68/push  "Ax"/imm32
+ 537     # . . call
+ 538     e8/call  trace-scan/disp32
+ 539     # . . discard args
+ 540     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 541     # no change in read index
+ 542     # . check-ints-equal(_test-trace-stream->read, 0, msg)
+ 543     # . . push args
+ 544     68/push  "F - test-trace-second-scan-starts-where-first-left-off"/imm32
+ 545     68/push  0/imm32
+ 546     b8/copy-to-EAX  _test-trace-stream/imm32
+ 547     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+ 548     # . . call
+ 549     e8/call check-ints-equal/disp32
+ 550     # . . discard args
+ 551     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 552     # pop into *Trace-stream
+ 553     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+ 554     # . end
+ 555     c3/return
+ 556 
+ 557 next-line-matches?:  # t : (address stream), line : (address string) -> result/EAX : boolean
+ 558     # pseudocode:
+ 559     #   while true:
+ 560     #     if (currl >= maxl) break
+ 561     #     if (currt >= maxt) return false
+ 562     #     if (*currt != *currl) return false
+ 563     #     ++currt
+ 564     #     ++currl
+ 565     #   return *currt == '\n'
+ 566     #
+ 567     # . prolog
+ 568     55/push-EBP
+ 569     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 570     # . save registers
+ 571     51/push-ECX
+ 572     52/push-EDX
+ 573     53/push-EBX
+ 574     56/push-ESI
+ 575     57/push-EDI
+ 576     # EDX = line
+ 577     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+ 578     # currl/ESI = line->data
+ 579     # . ESI = line/EDX->data
+ 580     8d/copy-address                 1/mod/*+disp8   2/rm32/EDX    .           .             .           6/r32/ESI   4/disp8         .                 # copy EDX+4 to ESI
+ 581     # maxl/ECX = line->data + line->size
+ 582     # . EAX = line/EDX->size
+ 583     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .                         0/r32/EAX   .               .                 # copy *EDX to EAX
+ 584     # . maxl/ECX = line->data/ESI + line->size/EAX
+ 585     8d/copy-address                 0/mod/indirect  4/rm32/sib    6/base/ESI  0/index/EAX   .           1/r32/ECX   .               .                 # copy EDX+EAX to ECX
+ 586     # EDI = t
+ 587     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+ 588     # EBX = t->data
+ 589     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy EDI+12 to EBX
+ 590     # maxt/EDX = t->data + t->write
+ 591     # . EAX = t->write
+ 592     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .                         0/r32/EAX   .               .                 # copy *EDI to EAX
+ 593     # . maxt/EDX = t->data/EBX + t->write/EAX
+ 594     8d/copy-address                 0/mod/indirect  4/rm32/sib    3/base/EBX  0/index/EAX   .           2/r32/EDX   .               .                 # copy EBX+EAX to EDX
+ 595     # currt/EDI = t->data + t->read
+ 596     # . EAX = t/EDI->read
+ 597     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .                         0/r32/EAX   4/disp8         .                 # copy *(EDI+4) to EAX
+ 598     # . currt/EDI = t->data/EBX + t->read/EAX
+ 599     8d/copy-address                 0/mod/indirect  4/rm32/sib    3/base/EBX  0/index/EAX   .           7/r32/EDI   .               .                 # copy EBX+EAX to EDI
+ 600 $next-line-matches?:loop:
+ 601     # if (currl/ESI >= maxl/ECX) break
+ 602     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI and ECX
+ 603     73/jump-if-greater-or-equal-unsigned  $next-line-matches?:break/disp8
+ 604     # if (currt/EDI >= maxt/EDX) return false
+ 605     # . EAX = false
+ 606     b8/copy-to-EAX  0/imm32/false
+ 607     39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDI and EDX
+ 608     73/jump-if-greater-or-equal-unsigned  $next-line-matches?:end/disp8
+ 609     # if (*currt/EDI != *currl/ESI) return false
+ 610     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 611     31/xor                          3/mod/direct    3/rm32/EAX    .           .             .           3/r32/EAX   .               .                 # clear EBX
+ 612     # . EAX = (char) *currt/EDI
+ 613     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .                         0/r32/EAX   .               .                 # copy *EDI to EAX
+ 614     # . EBX = (char) *currl/ESI
+ 615     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .                         3/r32/EBX   .               .                 # copy *ESI to EBX
+ 616     # . EAX >= EBX
+ 617     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
+ 618     # . EAX = false
+ 619     b8/copy-to-EAX  0/imm32/false
+ 620     75/jump-if-not-equal  $next-line-matches?:end/disp8
+ 621     # ++currt/EDI
+ 622     47/increment-EDI
+ 623     # ++currl/ESI
+ 624     46/increment-ESI
+ 625     eb/jump  $next-line-matches?:loop/disp8
+ 626 $next-line-matches?:break:
+ 627     # return *currt == '\n'
+ 628     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 629     # . EAX = (char) *currt
+ 630     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .                         0/r32/EAX   .               .                 # copy *EDI to EAX
+ 631     3d/compare-EAX-and  0xa/imm32/newline
+ 632     # . EAX = false
+ 633     b8/copy-to-EAX  1/imm32/true
+ 634     74/jump-if-equal  $next-line-matches?:end/disp8
+ 635     b8/copy-to-EAX  0/imm32/true
+ 636 $next-line-matches?:end:
+ 637     # . restore registers
+ 638     5f/pop-to-EDI
+ 639     5e/pop-to-ESI
+ 640     5b/pop-to-EBX
+ 641     5a/pop-to-EDX
+ 642     59/pop-to-ECX
+ 643     # . epilog
+ 644     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 645     5d/pop-to-EBP
+ 646     c3/return
+ 647 
+ 648 test-next-line-matches?-no-match-1:
+ 649     # next line of "ABABA" does not match "blah blah"
+ 650     # . EAX = next-line-matches?(_test-stream-line-ABABA, "blah blah")
+ 651     # . . push args
+ 652     68/push  "blah blah"/imm32
+ 653     68/push  _test-stream-line-ABABA/imm32
+ 654     # . . call
+ 655     e8/call  next-line-matches?/disp32
+ 656     # . . discard args
+ 657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 658     # . check-ints-equal(EAX, 0, msg)
+ 659     # . . push args
+ 660     68/push  "F - test-next-line-matches?-no-match-1"/imm32
+ 661     68/push  0/imm32
+ 662     50/push-EAX
+ 663     # . . call
+ 664     e8/call  check-ints-equal/disp32
+ 665     # . . discard args
+ 666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 667     c3/return
+ 668 
+ 669 test-next-line-matches?-no-match-2:
+ 670     # next line of "ABABA" does not match ""
+ 671     # . EAX = next-line-matches?(_test-stream-line-ABABA, "")
+ 672     # . . push args
+ 673     68/push  ""/imm32
+ 674     68/push  _test-stream-line-ABABA/imm32
+ 675     # . . call
+ 676     e8/call  next-line-matches?/disp32
+ 677     # . . discard args
+ 678     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 679     # . check-ints-equal(EAX, 0, msg)
+ 680     # . . push args
+ 681     68/push  "F - test-next-line-matches?-no-match-2"/imm32
+ 682     68/push  0/imm32
+ 683     50/push-EAX
+ 684     # . . call
+ 685     e8/call  check-ints-equal/disp32
+ 686     # . . discard args
+ 687     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 688     c3/return
+ 689 
+ 690 test-next-line-matches?-no-match-3:
+ 691     # next line of "ABABA" does not match  "AA"
+ 692     # . EAX = next-line-matches?(_test-stream-line-ABABA, "AA")
+ 693     # . . push args
+ 694     68/push  "AA"/imm32
+ 695     68/push  _test-stream-line-ABABA/imm32
+ 696     # . . call
+ 697     e8/call  next-line-matches?/disp32
+ 698     # . . discard args
+ 699     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 700     # . check-ints-equal(EAX, 0, msg)
+ 701     # . . push args
+ 702     68/push  "F - test-next-line-matches?-no-match-3"/imm32
+ 703     68/push  0/imm32
+ 704     50/push-EAX
+ 705     # . . call
+ 706     e8/call  check-ints-equal/disp32
+ 707     # . . discard args
+ 708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 709     c3/return
+ 710 
+ 711 test-next-line-matches?-match:
+ 712     # next line of "ABABA" matches "ABABA"
+ 713     # . EAX = next-line-matches?(_test-stream-line-ABABA, "ABABA")
+ 714     # . . push args
+ 715     68/push  "ABABA"/imm32
+ 716     68/push  _test-stream-line-ABABA/imm32
+ 717     # . . call
+ 718     e8/call  next-line-matches?/disp32
+ 719     # . . discard args
+ 720     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 721     # . check-ints-equal(EAX, 1, msg)
+ 722     # . . push args
+ 723     68/push  "F - test-next-line-matches?-match"/imm32
+ 724     68/push  1/imm32
+ 725     50/push-EAX
+ 726     # . . call
+ 727     e8/call  check-ints-equal/disp32
+ 728     # . . discard args
+ 729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 730     c3/return
+ 731 
+ 732 # move t->read to _after_ next newline
+ 733 skip-next-line:  # t : (address stream)
+ 734     # pseudocode:
+ 735     #   max = t->data + t->write
+ 736     #   i = t->read
+ 737     #   curr = t->data + t->read
+ 738     #   while true
+ 739     #     if (curr >= max) break
+ 740     #     ++i
+ 741     #     if (*curr == '\n') break
+ 742     #     ++curr
+ 743     #   t->read = i
+ 744     #
+ 745     # . prolog
+ 746     55/push-EBP
+ 747     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 748     # . save registers
+ 749     50/push-EAX
+ 750     51/push-ECX
+ 751     52/push-EDX
+ 752     53/push-EBX
+ 753     # ECX = t
+ 754     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+ 755     # EDX = t/ECX->data
+ 756     8d/copy-address                 1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy ECX+12 to EDX
+ 757     # EAX = t/ECX->write
+ 758     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+ 759     # max/EBX = t->data/EDX + t->write/EAX
+ 760     8d/copy-address                 0/mod/indirect  4/rm32/sib    2/base/EDX  0/index/EAX   .           3/r32/EBX   .               .                 # copy EDX+EAX to EBX
+ 761     # EAX = t/ECX->read
+ 762     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EDX
+ 763     # curr/ECX = t->data/EDX + t->read/EAX
+ 764     8d/copy-address                 0/mod/indirect  4/rm32/sib    2/base/EDX  0/index/EAX   .           1/r32/ECX   .               .                 # copy EDX+EAX to ECX
+ 765     # i/EDX = EAX
+ 766     8b/copy                         3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy EAX to EDX
+ 767 $skip-next-line:loop:
+ 768     # if (curr/ECX >= max/EBX) break
+ 769     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX and EBX
+ 770     73/jump-if-greater-or-equal-unsigned  $skip-next-line:end/disp8
+ 771     # ++i/EDX
+ 772     42/increment-EDX
+ 773     # if (*curr/ECX == '\n') break
+ 774     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 775     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+ 776     3d/compare-EAX-and  0a/imm32/newline
+ 777     74/jump-if-equal  $skip-next-line:end/disp8
+ 778     # ++curr/ECX
+ 779     41/increment-ECX
+ 780     # loop
+ 781     eb/jump  $skip-next-line:loop/disp8
+ 782 $skip-next-line:end:
+ 783     # ECX = t
+ 784     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+ 785     # t/ECX->read = i/EDX
+ 786     89/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy EDX to *(ECX+4)
+ 787     # . restore registers
+ 788     5b/pop-to-EBX
+ 789     5a/pop-to-EDX
+ 790     59/pop-to-ECX
+ 791     58/pop-to-EAX
+ 792     # . epilog
+ 793     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 794     5d/pop-to-EBP
+ 795     c3/return
+ 796 
+ 797 test-skip-next-line-empty:
+ 798     # skipping next line in empty stream leaves read pointer at 0
+ 799     # . skip-next-line(_test-stream-empty)
+ 800     # . . push args
+ 801     68/push  _test-stream-empty/imm32
+ 802     # . . call
+ 803     e8/call  skip-next-line/disp32
+ 804     # . . discard args
+ 805     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 806     # . check-ints-equal(_test-stream-empty->read, 0, msg)
+ 807     # . . push args
+ 808     68/push  "F - test-skip-next-line-empty"/imm32
+ 809     68/push  0/imm32
+ 810     b8/copy-to-EAX  _test-stream-empty/imm32
+ 811     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(EAX+4) to EAX
+ 812     50/push-EAX
+ 813     # . . call
+ 814     e8/call  check-ints-equal/disp32
+ 815     # . . discard args
+ 816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 817     c3/return
+ 818 
+ 819 test-skip-next-line-filled:
+ 820     # skipping next line increments read pointer by length of line + 1 (for newline)
+ 821     # . skip-next-line(_test-stream-filled)
+ 822     # . . push args
+ 823     68/push  _test-stream-filled/imm32
+ 824     # . . call
+ 825     e8/call  skip-next-line/disp32
+ 826     # . . discard args
+ 827     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 828     # . check-ints-equal(_test-stream-filled->read, 5, msg)
+ 829     # . . push args
+ 830     68/push  "F - test-skip-next-line-filled"/imm32
+ 831     68/push  5/imm32
+ 832     b8/copy-to-EAX  _test-stream-filled/imm32
+ 833     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(EAX+4) to EAX
+ 834     50/push-EAX
+ 835     # . . call
+ 836     e8/call  check-ints-equal/disp32
+ 837     # . . discard args
+ 838     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 839     c3/return
+ 840 
+ 841 clear-trace-stream:
+ 842     # . prolog
+ 843     55/push-EBP
+ 844     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 845     # . save registers
+ 846     50/push-EAX
+ 847     51/push-ECX
+ 848     # EAX = *Trace-stream
+ 849     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
+ 850     # ECX = t->length
+ 851     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EAX+8) to ECX
+ 852     # ECX = &t->data[t->length]
+ 853     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EAX+ECX+12 to ECX
+ 854     # t->write = 0
+ 855     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+ 856     # t->read = 0
+ 857     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
+ 858     # EAX = t->data
+ 859     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xc/imm32         # add to EAX
+ 860 $clear-trace-stream:loop:
+ 861     # if (EAX >= ECX) break
+ 862     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+ 863     73/jump-if-greater-or-equal-unsigned  $clear-trace-stream:end/disp8
+ 864     # *EAX = 0
+ 865     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+ 866     # EAX += 4
+ 867     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm32           # add to EAX
+ 868     eb/jump  $clear-trace-stream:loop/disp8
+ 869 $clear-trace-stream:end:
+ 870     # . restore registers
+ 871     59/pop-to-ECX
+ 872     58/pop-to-EAX
+ 873     # . epilog
+ 874     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 875     5d/pop-to-EBP
+ 876     c3/return
+ 877 
+ 878 # - helpers
+ 879 
+ 880 # 3-argument variant of _append
+ 881 _append-3:  # out : address, outend : address, s : (array byte) -> num_bytes_appended/EAX
+ 882     # . prolog
+ 883     55/push-EBP
+ 884     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 885     # . save registers
+ 886     51/push-ECX
+ 887     # EAX = _append-4(out, outend, &s->data[0], &s->data[s->length])
+ 888     # . . push &s->data[s->length]
+ 889     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+ 890     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+ 891     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+ 892     51/push-ECX
+ 893     # . . push &s->data[0]
+ 894     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
+ 895     51/push-ECX
+ 896     # . . push outend
+ 897     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 898     # . . push out
+ 899     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 900     # . . call
+ 901     e8/call  _append-4/disp32
+ 902     # . . discard args
+ 903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+ 904 $_append-3:end:
+ 905     # . restore registers
+ 906     59/pop-to-ECX
+ 907     # . epilog
+ 908     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 909     5d/pop-to-EBP
+ 910     c3/return
+ 911 
+ 912 # 4-argument variant of _append
+ 913 _append-4:  # out : address, outend : address, in : address, inend : address -> num_bytes_appended/EAX
+ 914     # . prolog
+ 915     55/push-EBP
+ 916     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 917     # . save registers
+ 918     51/push-ECX
+ 919     52/push-EDX
+ 920     53/push-EBX
+ 921     56/push-ESI
+ 922     57/push-EDI
+ 923     # EAX/num_bytes_appended = 0
+ 924     b8/copy-to-EAX  0/imm32
+ 925     # EDI = out
+ 926     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+ 927     # EDX = outend
+ 928     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+ 929     # ESI = in
+ 930     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0x10/disp8      .                 # copy *(EBP+16) to ESI
+ 931     # ECX = inend
+ 932     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
+ 933 $_append-4:loop:
+ 934     # if (in >= inend) break
+ 935     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI with ECX
+ 936     73/jump-if-greater-or-equal-unsigned  $_append-4:end/disp8
+ 937     # if (out >= outend) abort  # just to catch test failures fast
+ 938     39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDI with EDX
+ 939     73/jump-if-greater-or-equal-unsigned  $_append-4:abort/disp8
+ 940     # *out = *in
+ 941     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/BL    .               .                 # copy byte at *ESI to BL
+ 942     88/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *EDI
+ 943     # ++num_bytes_appended
+ 944     40/increment-EAX
+ 945     # ++in
+ 946     46/increment-ESI
+ 947     # ++out
+ 948     47/increment-EDI
+ 949     eb/jump  $_append-4:loop/disp8
+ 950 $_append-4:end:
+ 951     # . restore registers
+ 952     5f/pop-to-EDI
+ 953     5e/pop-to-ESI
+ 954     5b/pop-to-EBX
+ 955     5a/pop-to-EDX
+ 956     59/pop-to-ECX
+ 957     # . epilog
+ 958     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 959     5d/pop-to-EBP
+ 960     c3/return
+ 961 
+ 962 $_append-4:abort:
+ 963     # . _write(2/stderr, error)
+ 964     # . . push args
+ 965     68/push  "stream overflow\n"/imm32
+ 966     68/push  2/imm32/stderr
+ 967     # . . call
+ 968     e8/call  _write/disp32
+ 969     # . . discard args
+ 970     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 971     # . syscall(exit, 1)
+ 972     bb/copy-to-EBX  1/imm32
+ 973     b8/copy-to-EAX  1/imm32/exit
+ 974     cd/syscall  0x80/imm8
+ 975     # never gets here
+ 976 
+ 977 == data
+ 978 
+ 979 _test-stream-line-ABABA:
+ 980     # write
+ 981     8/imm32
+ 982     # read
+ 983     0/imm32
+ 984     # length
+ 985     8/imm32
+ 986     # data
+ 987     41 42 41 42 41 0A 00 00  # 8 bytes
+ 988 
+ 989 _test-stream-empty:
+ 990     # write
+ 991     0/imm32
+ 992     # read
+ 993     0/imm32
+ 994     # length
+ 995     8/imm32
+ 996     # data
+ 997     00 00 00 00 00 00 00 00  # 8 bytes
+ 998 
+ 999 _test-stream-filled:
+1000     # write
+1001     8/imm32
+1002     # read
+1003     0/imm32
+1004     # length
+1005     8/imm32
+1006     # data
+1007     41 41 41 41 0A 41 41 41  # 8 bytes
+1008 
+1009 # . . vim:nowrap:textwidth=0
 
diff --git a/html/subx/057write.subx.html b/html/subx/057write.subx.html index f63f2d25..4ab4e83d 100644 --- a/html/subx/057write.subx.html +++ b/html/subx/057write.subx.html @@ -3,8 +3,8 @@ Mu - subx/057write.subx - - + + @@ -15,14 +15,14 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } .subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.LineNr { } .subxTest { color: #5f8700; } -.Constant { color: #008787; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -119,7 +119,7 @@ if ('onhashchange' in window) { 59 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy ECX+EDX+12 to EBX 60 53/push-EBX 61 # . . call - 62 e8/call _append-3/disp32 + 62 e8/call _append-3/disp32 63 # . . discard args 64 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 65 # f->write += EAX diff --git a/html/subx/058stream-equal.subx.html b/html/subx/058stream-equal.subx.html index 57a27354..3f297e1a 100644 --- a/html/subx/058stream-equal.subx.html +++ b/html/subx/058stream-equal.subx.html @@ -3,8 +3,8 @@ Mu - subx/058stream-equal.subx - - + + @@ -14,15 +14,14 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxTest { color: #5f8700; } --> @@ -39,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -65,599 +64,592 @@ if ('onhashchange' in window) { 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 a single test, while debugging - 9 #? e8/call test-next-stream-line-equal-stops-at-newline/disp32 - 10 #? # syscall(exit, Num-test-failures) - 11 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 12 #? b8/copy-to-EAX 1/imm32/exit - 13 #? cd/syscall 0x80/imm8 - 14 - 15 # compare all the data in a stream (ignoring the read pointer) - 16 stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boolean - 17 # . prolog - 18 55/push-EBP - 19 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 20 # . save registers - 21 51/push-ECX - 22 52/push-EDX - 23 56/push-ESI - 24 57/push-EDI - 25 # ESI = f - 26 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - 27 # EAX = f->write - 28 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 29 # max/EDX = f->data + f->write - 30 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 2/r32/EDX 0xc/disp8 . # copy ESI+EAX+12 to EDX - 31 # currf/ESI = f->data - 32 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 0xc/imm32 # add to ESI - 33 # EDI = s - 34 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - 35 # if (f->write != s->length) return false - 36 $stream-data-equal?:compare-lengths: - 37 39/compare 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # compare *EDI and EAX - 38 75/jump-if-not-equal $stream-data-equal?:false/disp8 - 39 # currs/EDI = s->data - 40 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - 41 # EAX = ECX = 0 - 42 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 43 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - 44 $stream-data-equal?:loop: - 45 # if (curr >= max) return true - 46 39/compare 3/mod/direct 6/rm32/ESI . . . 2/r32/EDX . . # compare ESI with EDX - 47 7d/jump-if-greater-or-equal $stream-data-equal?:true/disp8 - 48 # AL = *currs - 49 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - 50 # CL = *curr - 51 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 1/r32/CL . . # copy byte at *EDI to CL - 52 # if (EAX != ECX) return false - 53 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX and ECX - 54 75/jump-if-not-equal $stream-data-equal?:false/disp8 - 55 # ++f - 56 46/increment-ESI - 57 # ++curr - 58 47/increment-EDI - 59 eb/jump $stream-data-equal?:loop/disp8 - 60 $stream-data-equal?:false: - 61 b8/copy-to-EAX 0/imm32 - 62 eb/jump $stream-data-equal?:end/disp8 - 63 $stream-data-equal?:true: - 64 b8/copy-to-EAX 1/imm32 - 65 $stream-data-equal?:end: - 66 # . restore registers - 67 5f/pop-to-EDI - 68 5e/pop-to-ESI - 69 5a/pop-to-EDX - 70 59/pop-to-ECX - 71 # . epilog - 72 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 73 5d/pop-to-EBP - 74 c3/return - 75 - 76 test-stream-data-equal: - 77 # . prolog - 78 55/push-EBP - 79 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 80 # clear-stream(_test-stream) + 8 # compare all the data in a stream (ignoring the read pointer) + 9 stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boolean + 10 # . prolog + 11 55/push-EBP + 12 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 13 # . save registers + 14 51/push-ECX + 15 52/push-EDX + 16 56/push-ESI + 17 57/push-EDI + 18 # ESI = f + 19 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 20 # EAX = f->write + 21 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX + 22 # maxf/EDX = f->data + f->write + 23 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 2/r32/EDX 0xc/disp8 . # copy ESI+EAX+12 to EDX + 24 # currf/ESI = f->data + 25 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 0xc/imm32 # add to ESI + 26 # EDI = s + 27 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + 28 # if (f->write != s->length) return false + 29 $stream-data-equal?:compare-lengths: + 30 39/compare 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # compare *EDI and EAX + 31 75/jump-if-not-equal $stream-data-equal?:false/disp8 + 32 # currs/EDI = s->data + 33 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI + 34 # EAX = ECX = 0 + 35 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 36 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX + 37 $stream-data-equal?:loop: + 38 # if (currf >= maxf) return true + 39 39/compare 3/mod/direct 6/rm32/ESI . . . 2/r32/EDX . . # compare ESI with EDX + 40 73/jump-if-greater-or-equal-unsigned $stream-data-equal?:true/disp8 + 41 # AL = *currs + 42 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL + 43 # CL = *curr + 44 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 1/r32/CL . . # copy byte at *EDI to CL + 45 # if (EAX != ECX) return false + 46 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX and ECX + 47 75/jump-if-not-equal $stream-data-equal?:false/disp8 + 48 # ++f + 49 46/increment-ESI + 50 # ++curr + 51 47/increment-EDI + 52 eb/jump $stream-data-equal?:loop/disp8 + 53 $stream-data-equal?:false: + 54 b8/copy-to-EAX 0/imm32 + 55 eb/jump $stream-data-equal?:end/disp8 + 56 $stream-data-equal?:true: + 57 b8/copy-to-EAX 1/imm32 + 58 $stream-data-equal?:end: + 59 # . restore registers + 60 5f/pop-to-EDI + 61 5e/pop-to-ESI + 62 5a/pop-to-EDX + 63 59/pop-to-ECX + 64 # . epilog + 65 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 66 5d/pop-to-EBP + 67 c3/return + 68 + 69 test-stream-data-equal: + 70 # . prolog + 71 55/push-EBP + 72 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 73 # clear-stream(_test-stream) + 74 # . . push args + 75 68/push _test-stream/imm32 + 76 # . . call + 77 e8/call clear-stream/disp32 + 78 # . . discard args + 79 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 80 # write(_test-stream, "Abc") 81 # . . push args - 82 68/push _test-stream/imm32 - 83 # . . call - 84 e8/call clear-stream/disp32 - 85 # . . discard args - 86 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 87 # write(_test-stream, "Abc") - 88 # . . push args - 89 68/push "Abc"/imm32 - 90 68/push _test-stream/imm32 - 91 # . . call - 92 e8/call write/disp32 - 93 # . . discard args - 94 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 95 # EAX = stream-data-equal?(_test-stream, "Abc") - 96 # . . push args - 97 68/push "Abc"/imm32 - 98 68/push _test-stream/imm32 - 99 # . . call -100 e8/call stream-data-equal?/disp32 -101 # . . discard args -102 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -103 # check-ints-equal(EAX, 1, msg) -104 # . . push args -105 68/push "F - test-stream-data-equal"/imm32 -106 68/push 1/imm32 -107 50/push-EAX -108 # . . call -109 e8/call check-ints-equal/disp32 -110 # . . discard args -111 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -112 # . epilog -113 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -114 5d/pop-to-EBP -115 c3/return -116 -117 test-stream-data-equal-2: -118 # . prolog -119 55/push-EBP -120 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -121 # clear-stream(_test-stream) + 82 68/push "Abc"/imm32 + 83 68/push _test-stream/imm32 + 84 # . . call + 85 e8/call write/disp32 + 86 # . . discard args + 87 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 88 # EAX = stream-data-equal?(_test-stream, "Abc") + 89 # . . push args + 90 68/push "Abc"/imm32 + 91 68/push _test-stream/imm32 + 92 # . . call + 93 e8/call stream-data-equal?/disp32 + 94 # . . discard args + 95 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 96 # check-ints-equal(EAX, 1, msg) + 97 # . . push args + 98 68/push "F - test-stream-data-equal"/imm32 + 99 68/push 1/imm32 +100 50/push-EAX +101 # . . call +102 e8/call check-ints-equal/disp32 +103 # . . discard args +104 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +105 # . epilog +106 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +107 5d/pop-to-EBP +108 c3/return +109 +110 test-stream-data-equal-2: +111 # . prolog +112 55/push-EBP +113 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +114 # clear-stream(_test-stream) +115 # . . push args +116 68/push _test-stream/imm32 +117 # . . call +118 e8/call clear-stream/disp32 +119 # . . discard args +120 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +121 # write(_test-stream, "Abc") 122 # . . push args -123 68/push _test-stream/imm32 -124 # . . call -125 e8/call clear-stream/disp32 -126 # . . discard args -127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -128 # write(_test-stream, "Abc") -129 # . . push args -130 68/push "Abc"/imm32 -131 68/push _test-stream/imm32 -132 # . . call -133 e8/call write/disp32 -134 # . . discard args -135 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -136 # EAX = stream-data-equal?(_test-stream, "Abd") -137 # . . push args -138 68/push "Abd"/imm32 -139 68/push _test-stream/imm32 -140 # . . call -141 e8/call stream-data-equal?/disp32 -142 # . . discard args -143 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -144 # check-ints-equal(EAX, 0, msg) -145 # . . push args -146 68/push "F - test-stream-data-equal-2"/imm32 -147 68/push 0/imm32 -148 50/push-EAX -149 # . . call -150 e8/call check-ints-equal/disp32 -151 # . . discard args -152 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -153 # . epilog -154 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -155 5d/pop-to-EBP -156 c3/return -157 -158 test-stream-data-equal-length-check: -159 # . prolog -160 55/push-EBP -161 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -162 # clear-stream(_test-stream) +123 68/push "Abc"/imm32 +124 68/push _test-stream/imm32 +125 # . . call +126 e8/call write/disp32 +127 # . . discard args +128 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +129 # EAX = stream-data-equal?(_test-stream, "Abd") +130 # . . push args +131 68/push "Abd"/imm32 +132 68/push _test-stream/imm32 +133 # . . call +134 e8/call stream-data-equal?/disp32 +135 # . . discard args +136 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +137 # check-ints-equal(EAX, 0, msg) +138 # . . push args +139 68/push "F - test-stream-data-equal-2"/imm32 +140 68/push 0/imm32 +141 50/push-EAX +142 # . . call +143 e8/call check-ints-equal/disp32 +144 # . . discard args +145 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +146 # . epilog +147 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +148 5d/pop-to-EBP +149 c3/return +150 +151 test-stream-data-equal-length-check: +152 # . prolog +153 55/push-EBP +154 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +155 # clear-stream(_test-stream) +156 # . . push args +157 68/push _test-stream/imm32 +158 # . . call +159 e8/call clear-stream/disp32 +160 # . . discard args +161 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +162 # write(_test-stream, "Abc") 163 # . . push args -164 68/push _test-stream/imm32 -165 # . . call -166 e8/call clear-stream/disp32 -167 # . . discard args -168 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -169 # write(_test-stream, "Abc") -170 # . . push args -171 68/push "Abc"/imm32 -172 68/push _test-stream/imm32 -173 # . . call -174 e8/call write/disp32 -175 # . . discard args -176 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -177 # EAX = stream-data-equal?(_test-stream, "Abcd") -178 # . . push args -179 68/push "Abcd"/imm32 -180 68/push _test-stream/imm32 -181 # . . call -182 e8/call stream-data-equal?/disp32 -183 # . . discard args -184 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -185 # check-ints-equal(EAX, 0, msg) -186 # . . push args -187 68/push "F - test-stream-data-equal-length-check"/imm32 -188 68/push 0/imm32 -189 50/push-EAX -190 # . . call -191 e8/call check-ints-equal/disp32 -192 # . . discard args -193 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -194 # . epilog -195 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -196 5d/pop-to-EBP -197 c3/return -198 -199 # helper for later tests -200 check-stream-equal: # f : (address stream), s : (address string), msg : (address string) -201 # . prolog -202 55/push-EBP -203 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -204 # . save registers -205 50/push-EAX -206 # EAX = stream-data-equal?(f, s) -207 # . . push args -208 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -209 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -210 # . . call -211 e8/call stream-data-equal?/disp32 -212 # . . discard args -213 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -214 # check-ints-equal(EAX, 1, msg) -215 # . . push args -216 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -217 68/push 1/imm32 -218 50/push-EAX -219 # . . call -220 e8/call check-ints-equal/disp32 -221 # . . discard args -222 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -223 $check-stream-equal:end: -224 # . restore registers -225 58/pop-to-EAX -226 # . epilog -227 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -228 5d/pop-to-EBP -229 c3/return -230 -231 # scan the next line until newline starting from f->read and compare it with -232 # 's' (ignoring the trailing newline) -233 # on success, set f->read to after the next newline -234 # on failure, leave f->read unmodified -235 # this function is usually used only in tests, so we repeatedly write f->read -236 next-stream-line-equal?: # f : (address stream), s : (address string) -> EAX : boolean -237 # pseudocode: -238 # currf = f->read # bound: f->write -239 # currs = 0 # bound : s->length -240 # while true -241 # if currf >= f->write -242 # return currs >= s->length -243 # if f[currf] == '\n' -244 # ++currf -245 # return currs >= s->length -246 # if (currs >= s->length) return false # the current line of f still has data to match -247 # if (f[currf] != s[currs]) return false -248 # ++currf -249 # ++currs -250 # -251 # collapsing the two branches that can return true: -252 # currf = f->read # bound: f->write -253 # currs = 0 # bound : s->length -254 # while true -255 # if (currf >= f->write) break -256 # if (f[currf] == '\n') break -257 # if (currs >= s->length) return false # the current line of f still has data to match -258 # if (f[currf] != s[currs]) return false -259 # ++currf -260 # ++currs -261 # ++currf # skip '\n' -262 # return currs >= s->length -263 # Here the final `++currf` is sometimes unnecessary (if we're already at the end of the stream) -264 # -265 # registers: -266 # f: ESI -267 # s: EDI -268 # currf: ECX -269 # currs: EDX -270 # f[currf]: EAX -271 # s[currs]: EBX -272 # -273 # . prolog -274 55/push-EBP -275 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -276 # . save registers -277 51/push-ECX -278 52/push-EDX -279 56/push-ESI -280 57/push-EDI -281 # ESI = f -282 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -283 # currf/ECX = f->read -284 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX -285 # EDI = s -286 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -287 # currs/EDX = 0 -288 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX -289 # EAX = EBX = 0 -290 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -291 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -292 $next-stream-line-equal?:loop: -293 # if (currf >= f->write) break -294 3b/compare 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # compare ECX with *ESI -295 7d/jump-if-greater-or-equal $next-stream-line-equal?:break/disp8 -296 # AL = *(f->data + f->read) -297 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL -298 # if (EAX == '\n') break -299 3d/compare-EAX-and 0xa/imm32/newline -300 74/jump-if-equal $next-stream-line-equal?:break/disp8 -301 # if (currs >= s->length) return false -302 3b/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare EDX with *EDI -303 7d/jump-if-greater-or-equal $next-stream-line-equal?:false/disp8 -304 # BL = *(s->data + currs) -305 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/BL 4/disp8 . # copy byte at *(EDI+EDX+4) to BL -306 # if (EAX != EBX) return false -307 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX -308 75/jump-if-not-equal $next-stream-line-equal?:false/disp8 -309 # ++currf -310 41/increment-ECX -311 # ++currs -312 42/increment-EDX -313 eb/jump $next-stream-line-equal?:loop/disp8 -314 $next-stream-line-equal?:break: -315 # ++currf -316 41/increment-ECX -317 # if (currs >= s->length) return true -318 3b/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare EDX with *EDI -319 7c/jump-if-lesser $next-stream-line-equal?:false/disp8 -320 $next-stream-line-equal?:true: -321 b8/copy-to-EAX 1/imm32 -322 # persist f->read on success -323 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) -324 eb/jump $next-stream-line-equal?:end/disp8 -325 $next-stream-line-equal?:false: -326 b8/copy-to-EAX 0/imm32 -327 $next-stream-line-equal?:end: -328 # . restore registers -329 5f/pop-to-EDI -330 5e/pop-to-ESI -331 5a/pop-to-EDX -332 59/pop-to-ECX -333 # . epilog -334 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -335 5d/pop-to-EBP -336 c3/return -337 -338 test-next-stream-line-equal-stops-at-newline: -339 # . prolog -340 55/push-EBP -341 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -342 # clear-stream(_test-stream) +164 68/push "Abc"/imm32 +165 68/push _test-stream/imm32 +166 # . . call +167 e8/call write/disp32 +168 # . . discard args +169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +170 # EAX = stream-data-equal?(_test-stream, "Abcd") +171 # . . push args +172 68/push "Abcd"/imm32 +173 68/push _test-stream/imm32 +174 # . . call +175 e8/call stream-data-equal?/disp32 +176 # . . discard args +177 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +178 # check-ints-equal(EAX, 0, msg) +179 # . . push args +180 68/push "F - test-stream-data-equal-length-check"/imm32 +181 68/push 0/imm32 +182 50/push-EAX +183 # . . call +184 e8/call check-ints-equal/disp32 +185 # . . discard args +186 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +187 # . epilog +188 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +189 5d/pop-to-EBP +190 c3/return +191 +192 # helper for later tests +193 check-stream-equal: # f : (address stream), s : (address string), msg : (address string) +194 # . prolog +195 55/push-EBP +196 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +197 # . save registers +198 50/push-EAX +199 # EAX = stream-data-equal?(f, s) +200 # . . push args +201 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +202 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +203 # . . call +204 e8/call stream-data-equal?/disp32 +205 # . . discard args +206 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +207 # check-ints-equal(EAX, 1, msg) +208 # . . push args +209 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +210 68/push 1/imm32 +211 50/push-EAX +212 # . . call +213 e8/call check-ints-equal/disp32 +214 # . . discard args +215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +216 $check-stream-equal:end: +217 # . restore registers +218 58/pop-to-EAX +219 # . epilog +220 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +221 5d/pop-to-EBP +222 c3/return +223 +224 # scan the next line until newline starting from f->read and compare it with +225 # 's' (ignoring the trailing newline) +226 # on success, set f->read to after the next newline +227 # on failure, leave f->read unmodified +228 # this function is usually used only in tests, so we repeatedly write f->read +229 next-stream-line-equal?: # f : (address stream), s : (address string) -> EAX : boolean +230 # pseudocode: +231 # currf = f->read # bound: f->write +232 # currs = 0 # bound : s->length +233 # while true +234 # if currf >= f->write +235 # return currs >= s->length +236 # if f[currf] == '\n' +237 # ++currf +238 # return currs >= s->length +239 # if (currs >= s->length) return false # the current line of f still has data to match +240 # if (f[currf] != s[currs]) return false +241 # ++currf +242 # ++currs +243 # +244 # collapsing the two branches that can return true: +245 # currf = f->read # bound: f->write +246 # currs = 0 # bound : s->length +247 # while true +248 # if (currf >= f->write) break +249 # if (f[currf] == '\n') break +250 # if (currs >= s->length) return false # the current line of f still has data to match +251 # if (f[currf] != s[currs]) return false +252 # ++currf +253 # ++currs +254 # ++currf # skip '\n' +255 # return currs >= s->length +256 # Here the final `++currf` is sometimes unnecessary (if we're already at the end of the stream) +257 # +258 # registers: +259 # f: ESI +260 # s: EDI +261 # currf: ECX +262 # currs: EDX +263 # f[currf]: EAX +264 # s[currs]: EBX +265 # +266 # . prolog +267 55/push-EBP +268 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +269 # . save registers +270 51/push-ECX +271 52/push-EDX +272 56/push-ESI +273 57/push-EDI +274 # ESI = f +275 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +276 # currf/ECX = f->read +277 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX +278 # EDI = s +279 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI +280 # currs/EDX = 0 +281 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX +282 # EAX = EBX = 0 +283 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +284 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX +285 $next-stream-line-equal?:loop: +286 # if (currf >= f->write) break +287 3b/compare 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # compare ECX with *ESI +288 7d/jump-if-greater-or-equal $next-stream-line-equal?:break/disp8 +289 # AL = *(f->data + f->read) +290 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL +291 # if (EAX == '\n') break +292 3d/compare-EAX-and 0xa/imm32/newline +293 74/jump-if-equal $next-stream-line-equal?:break/disp8 +294 # if (currs >= s->length) return false +295 3b/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare EDX with *EDI +296 7d/jump-if-greater-or-equal $next-stream-line-equal?:false/disp8 +297 # BL = *(s->data + currs) +298 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/BL 4/disp8 . # copy byte at *(EDI+EDX+4) to BL +299 # if (EAX != EBX) return false +300 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX +301 75/jump-if-not-equal $next-stream-line-equal?:false/disp8 +302 # ++currf +303 41/increment-ECX +304 # ++currs +305 42/increment-EDX +306 eb/jump $next-stream-line-equal?:loop/disp8 +307 $next-stream-line-equal?:break: +308 # ++currf +309 41/increment-ECX +310 # if (currs >= s->length) return true +311 3b/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare EDX with *EDI +312 7c/jump-if-lesser $next-stream-line-equal?:false/disp8 +313 $next-stream-line-equal?:true: +314 b8/copy-to-EAX 1/imm32 +315 # persist f->read on success +316 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) +317 eb/jump $next-stream-line-equal?:end/disp8 +318 $next-stream-line-equal?:false: +319 b8/copy-to-EAX 0/imm32 +320 $next-stream-line-equal?:end: +321 # . restore registers +322 5f/pop-to-EDI +323 5e/pop-to-ESI +324 5a/pop-to-EDX +325 59/pop-to-ECX +326 # . epilog +327 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +328 5d/pop-to-EBP +329 c3/return +330 +331 test-next-stream-line-equal-stops-at-newline: +332 # . prolog +333 55/push-EBP +334 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +335 # clear-stream(_test-stream) +336 # . . push args +337 68/push _test-stream/imm32 +338 # . . call +339 e8/call clear-stream/disp32 +340 # . . discard args +341 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +342 # write(_test-stream, "Abc\ndef") 343 # . . push args -344 68/push _test-stream/imm32 -345 # . . call -346 e8/call clear-stream/disp32 -347 # . . discard args -348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -349 # write(_test-stream, "Abc\ndef") -350 # . . push args -351 68/push "Abc\ndef"/imm32 -352 68/push _test-stream/imm32 -353 # . . call -354 e8/call write/disp32 -355 # . . discard args -356 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -357 # EAX = next-stream-line-equal?(_test-stream, "Abc") -358 # . . push args -359 68/push "Abc"/imm32 -360 68/push _test-stream/imm32 -361 # . . call -362 e8/call next-stream-line-equal?/disp32 -363 # . . discard args -364 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -365 # check-ints-equal(EAX, 1, msg) -366 # . . push args -367 68/push "F - test-next-stream-line-equal-stops-at-newline"/imm32 -368 68/push 1/imm32 -369 50/push-EAX -370 # . . call -371 e8/call check-ints-equal/disp32 -372 # . . discard args -373 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -374 # . epilog -375 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -376 5d/pop-to-EBP -377 c3/return -378 -379 test-next-stream-line-equal-stops-at-newline-2: -380 # . prolog -381 55/push-EBP -382 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -383 # clear-stream(_test-stream) +344 68/push "Abc\ndef"/imm32 +345 68/push _test-stream/imm32 +346 # . . call +347 e8/call write/disp32 +348 # . . discard args +349 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +350 # EAX = next-stream-line-equal?(_test-stream, "Abc") +351 # . . push args +352 68/push "Abc"/imm32 +353 68/push _test-stream/imm32 +354 # . . call +355 e8/call next-stream-line-equal?/disp32 +356 # . . discard args +357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +358 # check-ints-equal(EAX, 1, msg) +359 # . . push args +360 68/push "F - test-next-stream-line-equal-stops-at-newline"/imm32 +361 68/push 1/imm32 +362 50/push-EAX +363 # . . call +364 e8/call check-ints-equal/disp32 +365 # . . discard args +366 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +367 # . epilog +368 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +369 5d/pop-to-EBP +370 c3/return +371 +372 test-next-stream-line-equal-stops-at-newline-2: +373 # . prolog +374 55/push-EBP +375 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +376 # clear-stream(_test-stream) +377 # . . push args +378 68/push _test-stream/imm32 +379 # . . call +380 e8/call clear-stream/disp32 +381 # . . discard args +382 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +383 # write(_test-stream, "Abc\ndef") 384 # . . push args -385 68/push _test-stream/imm32 -386 # . . call -387 e8/call clear-stream/disp32 -388 # . . discard args -389 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -390 # write(_test-stream, "Abc\ndef") -391 # . . push args -392 68/push "Abc\ndef"/imm32 -393 68/push _test-stream/imm32 -394 # . . call -395 e8/call write/disp32 -396 # . . discard args -397 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -398 # EAX = next-stream-line-equal?(_test-stream, "def") -399 # . . push args -400 68/push "def"/imm32 -401 68/push _test-stream/imm32 -402 # . . call -403 e8/call next-stream-line-equal?/disp32 -404 # . . discard args -405 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -406 # check-ints-equal(EAX, 0, msg) -407 # . . push args -408 68/push "F - test-next-stream-line-equal-stops-at-newline-2"/imm32 -409 68/push 0/imm32 -410 50/push-EAX -411 # . . call -412 e8/call check-ints-equal/disp32 -413 # . . discard args -414 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -415 # . epilog -416 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -417 5d/pop-to-EBP -418 c3/return -419 -420 test-next-stream-line-equal-skips-newline: -421 # . prolog -422 55/push-EBP -423 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -424 # clear-stream(_test-stream) +385 68/push "Abc\ndef"/imm32 +386 68/push _test-stream/imm32 +387 # . . call +388 e8/call write/disp32 +389 # . . discard args +390 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +391 # EAX = next-stream-line-equal?(_test-stream, "def") +392 # . . push args +393 68/push "def"/imm32 +394 68/push _test-stream/imm32 +395 # . . call +396 e8/call next-stream-line-equal?/disp32 +397 # . . discard args +398 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +399 # check-ints-equal(EAX, 0, msg) +400 # . . push args +401 68/push "F - test-next-stream-line-equal-stops-at-newline-2"/imm32 +402 68/push 0/imm32 +403 50/push-EAX +404 # . . call +405 e8/call check-ints-equal/disp32 +406 # . . discard args +407 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +408 # . epilog +409 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +410 5d/pop-to-EBP +411 c3/return +412 +413 test-next-stream-line-equal-skips-newline: +414 # . prolog +415 55/push-EBP +416 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +417 # clear-stream(_test-stream) +418 # . . push args +419 68/push _test-stream/imm32 +420 # . . call +421 e8/call clear-stream/disp32 +422 # . . discard args +423 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +424 # write(_test-stream, "Abc\ndef\n") 425 # . . push args -426 68/push _test-stream/imm32 -427 # . . call -428 e8/call clear-stream/disp32 -429 # . . discard args -430 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -431 # write(_test-stream, "Abc\ndef\n") -432 # . . push args -433 68/push "Abc\ndef\n"/imm32 -434 68/push _test-stream/imm32 -435 # . . call -436 e8/call write/disp32 -437 # . . discard args -438 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -439 # next-stream-line-equal?(_test-stream, "Abc") -440 # . . push args -441 68/push "Abc"/imm32 -442 68/push _test-stream/imm32 -443 # . . call -444 e8/call next-stream-line-equal?/disp32 -445 # . . discard args -446 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -447 # EAX = next-stream-line-equal?(_test-stream, "def") -448 # . . push args -449 68/push "def"/imm32 -450 68/push _test-stream/imm32 -451 # . . call -452 e8/call next-stream-line-equal?/disp32 -453 # . . discard args -454 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -455 # check-ints-equal(EAX, 1, msg) -456 # . . push args -457 68/push "F - test-next-stream-line-equal-skips-newline"/imm32 -458 68/push 1/imm32 -459 50/push-EAX -460 # . . call -461 e8/call check-ints-equal/disp32 -462 # . . discard args -463 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -464 # . epilog -465 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -466 5d/pop-to-EBP -467 c3/return -468 -469 test-next-stream-line-equal-handles-final-line: -470 # . prolog -471 55/push-EBP -472 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -473 # clear-stream(_test-stream) +426 68/push "Abc\ndef\n"/imm32 +427 68/push _test-stream/imm32 +428 # . . call +429 e8/call write/disp32 +430 # . . discard args +431 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +432 # next-stream-line-equal?(_test-stream, "Abc") +433 # . . push args +434 68/push "Abc"/imm32 +435 68/push _test-stream/imm32 +436 # . . call +437 e8/call next-stream-line-equal?/disp32 +438 # . . discard args +439 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +440 # EAX = next-stream-line-equal?(_test-stream, "def") +441 # . . push args +442 68/push "def"/imm32 +443 68/push _test-stream/imm32 +444 # . . call +445 e8/call next-stream-line-equal?/disp32 +446 # . . discard args +447 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +448 # check-ints-equal(EAX, 1, msg) +449 # . . push args +450 68/push "F - test-next-stream-line-equal-skips-newline"/imm32 +451 68/push 1/imm32 +452 50/push-EAX +453 # . . call +454 e8/call check-ints-equal/disp32 +455 # . . discard args +456 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +457 # . epilog +458 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +459 5d/pop-to-EBP +460 c3/return +461 +462 test-next-stream-line-equal-handles-final-line: +463 # . prolog +464 55/push-EBP +465 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +466 # clear-stream(_test-stream) +467 # . . push args +468 68/push _test-stream/imm32 +469 # . . call +470 e8/call clear-stream/disp32 +471 # . . discard args +472 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +473 # write(_test-stream, "Abc\ndef") 474 # . . push args -475 68/push _test-stream/imm32 -476 # . . call -477 e8/call clear-stream/disp32 -478 # . . discard args -479 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -480 # write(_test-stream, "Abc\ndef") -481 # . . push args -482 68/push "Abc\ndef"/imm32 -483 68/push _test-stream/imm32 -484 # . . call -485 e8/call write/disp32 -486 # . . discard args -487 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -488 # next-stream-line-equal?(_test-stream, "Abc") -489 # . . push args -490 68/push "Abc"/imm32 -491 68/push _test-stream/imm32 -492 # . . call -493 e8/call next-stream-line-equal?/disp32 -494 # . . discard args -495 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -496 # EAX = next-stream-line-equal?(_test-stream, "def") -497 # . . push args -498 68/push "def"/imm32 -499 68/push _test-stream/imm32 -500 # . . call -501 e8/call next-stream-line-equal?/disp32 -502 # . . discard args -503 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -504 # check-ints-equal(EAX, 1, msg) -505 # . . push args -506 68/push "F - test-next-stream-line-equal-skips-newline"/imm32 -507 68/push 1/imm32 -508 50/push-EAX -509 # . . call -510 e8/call check-ints-equal/disp32 -511 # . . discard args -512 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -513 # . epilog -514 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -515 5d/pop-to-EBP -516 c3/return -517 -518 test-next-stream-line-equal-always-fails-after-Eof: -519 # . prolog -520 55/push-EBP -521 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -522 # clear-stream(_test-stream) -523 # . . push args -524 68/push _test-stream/imm32 -525 # . . call -526 e8/call clear-stream/disp32 -527 # . . discard args -528 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -529 # write nothing -530 # EAX = next-stream-line-equal?(_test-stream, "") -531 # . . push args -532 68/push ""/imm32 -533 68/push _test-stream/imm32 -534 # . . call -535 e8/call next-stream-line-equal?/disp32 -536 # . . discard args -537 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -538 # check-ints-equal(EAX, 0, msg) -539 # . . push args -540 68/push "F - test-next-stream-line-equal-always-fails-after-Eof"/imm32 -541 68/push 1/imm32 -542 50/push-EAX -543 # . . call -544 e8/call check-ints-equal/disp32 -545 # . . discard args -546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -547 # EAX = next-stream-line-equal?(_test-stream, "") -548 # . . push args -549 68/push ""/imm32 -550 68/push _test-stream/imm32 -551 # . . call -552 e8/call next-stream-line-equal?/disp32 -553 # . . discard args -554 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -555 # check-ints-equal(EAX, 0, msg) -556 # . . push args -557 68/push "F - test-next-stream-line-equal-always-fails-after-Eof/2"/imm32 -558 68/push 1/imm32 -559 50/push-EAX -560 # . . call -561 e8/call check-ints-equal/disp32 -562 # . . discard args -563 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -564 # . epilog -565 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -566 5d/pop-to-EBP -567 c3/return -568 -569 # helper for later tests -570 check-next-stream-line-equal: -571 # . prolog -572 55/push-EBP -573 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -574 # . save registers -575 50/push-EAX -576 # EAX = next-stream-line-equal?(f, s) -577 # . . push args -578 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -579 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -580 # . . call -581 e8/call next-stream-line-equal?/disp32 -582 # . . discard args -583 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -584 # check-ints-equal(EAX, 1, msg) -585 # . . push args -586 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -587 68/push 1/imm32 -588 50/push-EAX -589 # . . call -590 e8/call check-ints-equal/disp32 -591 # . . discard args -592 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -593 # . restore registers -594 58/pop-to-EAX -595 # . epilog -596 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -597 5d/pop-to-EBP -598 c3/return -599 -600 # . . vim:nowrap:textwidth=0 +475 68/push "Abc\ndef"/imm32 +476 68/push _test-stream/imm32 +477 # . . call +478 e8/call write/disp32 +479 # . . discard args +480 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +481 # next-stream-line-equal?(_test-stream, "Abc") +482 # . . push args +483 68/push "Abc"/imm32 +484 68/push _test-stream/imm32 +485 # . . call +486 e8/call next-stream-line-equal?/disp32 +487 # . . discard args +488 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +489 # EAX = next-stream-line-equal?(_test-stream, "def") +490 # . . push args +491 68/push "def"/imm32 +492 68/push _test-stream/imm32 +493 # . . call +494 e8/call next-stream-line-equal?/disp32 +495 # . . discard args +496 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +497 # check-ints-equal(EAX, 1, msg) +498 # . . push args +499 68/push "F - test-next-stream-line-equal-skips-newline"/imm32 +500 68/push 1/imm32 +501 50/push-EAX +502 # . . call +503 e8/call check-ints-equal/disp32 +504 # . . discard args +505 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +506 # . epilog +507 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +508 5d/pop-to-EBP +509 c3/return +510 +511 test-next-stream-line-equal-always-fails-after-Eof: +512 # . prolog +513 55/push-EBP +514 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +515 # clear-stream(_test-stream) +516 # . . push args +517 68/push _test-stream/imm32 +518 # . . call +519 e8/call clear-stream/disp32 +520 # . . discard args +521 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +522 # write nothing +523 # EAX = next-stream-line-equal?(_test-stream, "") +524 # . . push args +525 68/push ""/imm32 +526 68/push _test-stream/imm32 +527 # . . call +528 e8/call next-stream-line-equal?/disp32 +529 # . . discard args +530 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +531 # check-ints-equal(EAX, 0, msg) +532 # . . push args +533 68/push "F - test-next-stream-line-equal-always-fails-after-Eof"/imm32 +534 68/push 1/imm32 +535 50/push-EAX +536 # . . call +537 e8/call check-ints-equal/disp32 +538 # . . discard args +539 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +540 # EAX = next-stream-line-equal?(_test-stream, "") +541 # . . push args +542 68/push ""/imm32 +543 68/push _test-stream/imm32 +544 # . . call +545 e8/call next-stream-line-equal?/disp32 +546 # . . discard args +547 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +548 # check-ints-equal(EAX, 0, msg) +549 # . . push args +550 68/push "F - test-next-stream-line-equal-always-fails-after-Eof/2"/imm32 +551 68/push 1/imm32 +552 50/push-EAX +553 # . . call +554 e8/call check-ints-equal/disp32 +555 # . . discard args +556 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +557 # . epilog +558 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +559 5d/pop-to-EBP +560 c3/return +561 +562 # helper for later tests +563 check-next-stream-line-equal: +564 # . prolog +565 55/push-EBP +566 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +567 # . save registers +568 50/push-EAX +569 # EAX = next-stream-line-equal?(f, s) +570 # . . push args +571 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +572 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +573 # . . call +574 e8/call next-stream-line-equal?/disp32 +575 # . . discard args +576 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +577 # check-ints-equal(EAX, 1, msg) +578 # . . push args +579 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +580 68/push 1/imm32 +581 50/push-EAX +582 # . . call +583 e8/call check-ints-equal/disp32 +584 # . . discard args +585 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +586 # . restore registers +587 58/pop-to-EAX +588 # . epilog +589 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +590 5d/pop-to-EBP +591 c3/return +592 +593 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/059stop.subx.html b/html/subx/059stop.subx.html index 83fddf7d..cb1fe62e 100644 --- a/html/subx/059stop.subx.html +++ b/html/subx/059stop.subx.html @@ -3,8 +3,8 @@ Mu - subx/059stop.subx - - + + @@ -14,16 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } -.subxS1Comment { color: #0000af; } .CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.subxMinorFunction { color: #875f5f; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxTest { color: #5f8700; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -98,180 +98,173 @@ if ('onhashchange' in window) { 37 # . op subop mod rm32 base index scale r32 38 # . 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 39 - 40 #? Entry: # run a single test, while debugging - 41 #? e8/call test-stop-skips-returns-on-exit/disp32 - 42 #? # syscall(exit, Num-test-failures) - 43 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 44 #? b8/copy-to-EAX 1/imm32/exit - 45 #? cd/syscall 0x80/imm8 - 46 - 47 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to - 48 # the stack. - 49 # Ugly that we need to know the size of args, but so it goes. - 50 tailor-exit-descriptor: # ed : (address exit-descriptor), nbytes : int -> <void> - 51 # . prolog - 52 55/push-EBP - 53 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 54 # . save registers - 55 50/push-EAX - 56 51/push-ECX - 57 # EAX = nbytes - 58 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 59 # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor. - 60 # The return address for a call in the caller's body will be at: - 61 # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) - 62 # X-12 if the caller takes 8 bytes of args - 63 # ..and so on - 64 # That's the value we need to return: X-nbytes-4 - 65 # - 66 # However, we also need to account for the perturbance to ESP caused by the - 67 # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 - 68 # bytes for the return address and 4 bytes to push EBP above. - 69 # So EBP at this point is X-16. - 70 # - 71 # So the return address for the next call in the caller is: - 72 # EBP+8 if the caller takes 4 bytes of args - 73 # EBP+4 if the caller takes 8 bytes of args - 74 # EBP if the caller takes 12 bytes of args - 75 # EBP-4 if the caller takes 16 bytes of args - 76 # ..and so on - 77 # That's EBP+12-nbytes. - 78 # option 1: 6 + 3 bytes - 79 #? 2d/subtract 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # subtract from EAX - 80 #? 8d/copy-address 0/mod/indirect 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX . . # copy EBP+EAX to EAX - 81 # option 2: 2 + 4 bytes - 82 f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX - 83 8d/copy-address 1/mod/*+disp8 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EBP+EAX+12 to EAX - 84 # copy EAX to ed->target - 85 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - 86 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX - 87 # initialize ed->value - 88 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # copy to *(ECX+4) - 89 $tailor-exit-descriptor:end: - 90 # . restore registers - 91 59/pop-to-ECX - 92 58/pop-to-EAX - 93 # . epilog - 94 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 95 5d/pop-to-EBP - 96 c3/return - 97 - 98 stop: # ed : (address exit-descriptor), value : int - 99 # no prolog; one way or another, we're going to clobber registers -100 # EAX = ed -101 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX -102 # if (ed->target == 0) really exit -103 81 7/subop/compare 0/mod/indirect 0/rm32/EAX . . . . . 0/imm32 # compare *EAX -104 75/jump-if-not-equal $stop:fake/disp8 -105 # . syscall(exit, value) -106 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(ESP+8) to EBX -107 b8/copy-to-EAX 1/imm32/exit -108 cd/syscall 0x80/imm8 -109 $stop:fake: -110 # otherwise: -111 # ed->value = value+1 -112 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(ESP+8) to ECX -113 41/increment-ECX -114 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) -115 # perform a non-local jump to ed->target -116 8b/copy 0/mod/indirect 0/rm32/EAX . . . 4/r32/ESP . . # copy *EAX to ESP -117 $stop:end: -118 c3/return # doesn't return to caller -119 -120 test-stop-skips-returns-on-exit: -121 # This looks like the standard prolog, but is here for different reasons. -122 # A function calling 'stop' can't rely on EBP persisting past the call. -123 # -124 # Use EBP here as a stable base to refer to locals and arguments from in the -125 # presence of push/pop/call instructions. -126 # *Don't* use EBP as a way to restore ESP. -127 55/push-EBP -128 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -129 # Make room for an exit descriptor on the stack. That's almost always the -130 # right place for it, available only as long as it's legal to use. Once this -131 # containing function returns we'll need a new exit descriptor. -132 # var ed/EAX : (address exit-descriptor) -133 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -134 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -135 # Size the exit-descriptor precisely for the next call below, to _test-stop-1. -136 # tailor-exit-descriptor(ed, 4) -137 # . . push args -138 68/push 4/imm32/nbytes-of-args-for-_test-stop-1 + 40 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to + 41 # the stack. + 42 # Ugly that we need to know the size of args, but so it goes. + 43 tailor-exit-descriptor: # ed : (address exit-descriptor), nbytes : int -> <void> + 44 # . prolog + 45 55/push-EBP + 46 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 47 # . save registers + 48 50/push-EAX + 49 51/push-ECX + 50 # EAX = nbytes + 51 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 52 # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor. + 53 # The return address for a call in the caller's body will be at: + 54 # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) + 55 # X-12 if the caller takes 8 bytes of args + 56 # ..and so on + 57 # That's the value we need to return: X-nbytes-4 + 58 # + 59 # However, we also need to account for the perturbance to ESP caused by the + 60 # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 + 61 # bytes for the return address and 4 bytes to push EBP above. + 62 # So EBP at this point is X-16. + 63 # + 64 # So the return address for the next call in the caller is: + 65 # EBP+8 if the caller takes 4 bytes of args + 66 # EBP+4 if the caller takes 8 bytes of args + 67 # EBP if the caller takes 12 bytes of args + 68 # EBP-4 if the caller takes 16 bytes of args + 69 # ..and so on + 70 # That's EBP+12-nbytes. + 71 # option 1: 6 + 3 bytes + 72 #? 2d/subtract 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # subtract from EAX + 73 #? 8d/copy-address 0/mod/indirect 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX . . # copy EBP+EAX to EAX + 74 # option 2: 2 + 4 bytes + 75 f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX + 76 8d/copy-address 1/mod/*+disp8 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EBP+EAX+12 to EAX + 77 # copy EAX to ed->target + 78 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + 79 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX + 80 # initialize ed->value + 81 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # copy to *(ECX+4) + 82 $tailor-exit-descriptor:end: + 83 # . restore registers + 84 59/pop-to-ECX + 85 58/pop-to-EAX + 86 # . epilog + 87 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 88 5d/pop-to-EBP + 89 c3/return + 90 + 91 stop: # ed : (address exit-descriptor), value : int + 92 # no prolog; one way or another, we're going to clobber registers + 93 # EAX = ed + 94 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX + 95 # if (ed->target == 0) really exit + 96 81 7/subop/compare 0/mod/indirect 0/rm32/EAX . . . . . 0/imm32 # compare *EAX + 97 75/jump-if-not-equal $stop:fake/disp8 + 98 # . syscall(exit, value) + 99 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(ESP+8) to EBX +100 b8/copy-to-EAX 1/imm32/exit +101 cd/syscall 0x80/imm8 +102 $stop:fake: +103 # otherwise: +104 # ed->value = value+1 +105 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(ESP+8) to ECX +106 41/increment-ECX +107 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) +108 # perform a non-local jump to ed->target +109 8b/copy 0/mod/indirect 0/rm32/EAX . . . 4/r32/ESP . . # copy *EAX to ESP +110 $stop:end: +111 c3/return # doesn't return to caller +112 +113 test-stop-skips-returns-on-exit: +114 # This looks like the standard prolog, but is here for different reasons. +115 # A function calling 'stop' can't rely on EBP persisting past the call. +116 # +117 # Use EBP here as a stable base to refer to locals and arguments from in the +118 # presence of push/pop/call instructions. +119 # *Don't* use EBP as a way to restore ESP. +120 55/push-EBP +121 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +122 # Make room for an exit descriptor on the stack. That's almost always the +123 # right place for it, available only as long as it's legal to use. Once this +124 # containing function returns we'll need a new exit descriptor. +125 # var ed/EAX : (address exit-descriptor) +126 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +127 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +128 # Size the exit-descriptor precisely for the next call below, to _test-stop-1. +129 # tailor-exit-descriptor(ed, 4) +130 # . . push args +131 68/push 4/imm32/nbytes-of-args-for-_test-stop-1 +132 50/push-EAX +133 # . . call +134 e8/call tailor-exit-descriptor/disp32 +135 # . . discard args +136 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +137 # . _test-stop-1(ed) +138 # . . push args 139 50/push-EAX 140 # . . call -141 e8/call tailor-exit-descriptor/disp32 -142 # . . discard args -143 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -144 # . _test-stop-1(ed) -145 # . . push args -146 50/push-EAX -147 # . . call -148 e8/call _test-stop-1/disp32 -149 # registers except ESP may be clobbered at this point -150 # restore args -151 58/pop-to-EAX -152 # check that _test-stop-1 tried to call exit(1) -153 # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 -154 # . . push args -155 68/push "F - test-stop-skips-returns-on-exit"/imm32 -156 68/push 2/imm32 -157 # . . push ed->value -158 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -159 # . . call -160 e8/call check-ints-equal/disp32 -161 # . . discard args -162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -163 # . epilog -164 # don't restore ESP from EBP; manually reclaim locals -165 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -166 5d/pop-to-EBP -167 c3/return -168 -169 _test-stop-1: # ed : (address exit-descriptor) -170 # . prolog -171 55/push-EBP -172 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -173 # _test-stop-2(ed) -174 # . . push args -175 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -176 # . . call -177 e8/call _test-stop-2/disp32 -178 # should never get past this point -179 $_test-stop-1:dead-end: -180 # . . discard args -181 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -182 # signal test failed: check-ints-equal(1, 0, msg) -183 # . . push args -184 68/push "F - test-stop-skips-returns-on-exit"/imm32 -185 68/push 0/imm32 -186 68/push 1/imm32 -187 # . . call -188 e8/call check-ints-equal/disp32 -189 # . . discard args -190 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -191 # . epilog -192 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -193 5d/pop-to-EBP -194 c3/return -195 -196 _test-stop-2: # ed : (address exit-descriptor) -197 # . prolog -198 55/push-EBP -199 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -200 # . stop(ed, 1) -201 # . . push args -202 68/push 1/imm32 -203 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -204 # . . call -205 e8/call stop/disp32 -206 # should never get past this point -207 $_test-stop-2:dead-end: -208 # . epilog -209 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -210 5d/pop-to-EBP -211 c3/return -212 -213 # . . vim:nowrap:textwidth=0 +141 e8/call _test-stop-1/disp32 +142 # registers except ESP may be clobbered at this point +143 # restore args +144 58/pop-to-EAX +145 # check that _test-stop-1 tried to call exit(1) +146 # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 +147 # . . push args +148 68/push "F - test-stop-skips-returns-on-exit"/imm32 +149 68/push 2/imm32 +150 # . . push ed->value +151 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +152 # . . call +153 e8/call check-ints-equal/disp32 +154 # . . discard args +155 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +156 # . epilog +157 # don't restore ESP from EBP; manually reclaim locals +158 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +159 5d/pop-to-EBP +160 c3/return +161 +162 _test-stop-1: # ed : (address exit-descriptor) +163 # . prolog +164 55/push-EBP +165 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +166 # _test-stop-2(ed) +167 # . . push args +168 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +169 # . . call +170 e8/call _test-stop-2/disp32 +171 # should never get past this point +172 $_test-stop-1:dead-end: +173 # . . discard args +174 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +175 # signal test failed: check-ints-equal(1, 0, msg) +176 # . . push args +177 68/push "F - test-stop-skips-returns-on-exit"/imm32 +178 68/push 0/imm32 +179 68/push 1/imm32 +180 # . . call +181 e8/call check-ints-equal/disp32 +182 # . . discard args +183 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +184 # . epilog +185 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +186 5d/pop-to-EBP +187 c3/return +188 +189 _test-stop-2: # ed : (address exit-descriptor) +190 # . prolog +191 55/push-EBP +192 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +193 # . stop(ed, 1) +194 # . . push args +195 68/push 1/imm32 +196 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +197 # . . call +198 e8/call stop/disp32 +199 # should never get past this point +200 $_test-stop-2:dead-end: +201 # . epilog +202 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +203 5d/pop-to-EBP +204 c3/return +205 +206 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/060read.subx.html b/html/subx/060read.subx.html index 55c89134..0c42255d 100644 --- a/html/subx/060read.subx.html +++ b/html/subx/060read.subx.html @@ -3,8 +3,8 @@ Mu - subx/060read.subx - - + + @@ -14,16 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.subxTest { color: #5f8700; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -226,10 +226,10 @@ if ('onhashchange' in window) { 165 $_buffer-4:loop: 166 # if (in >= inend) break 167 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX -168 7d/jump-if-greater-or-equal $_buffer-4:end/disp8 +168 73/jump-if-greater-or-equal-unsigned $_buffer-4:end/disp8 169 # if (out >= outend) break # for now silently ignore filled up buffer 170 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX -171 7d/jump-if-greater-or-equal $_buffer-4:end/disp8 +171 73/jump-if-greater-or-equal-unsigned $_buffer-4:end/disp8 172 # *out = *in 173 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL 174 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI @@ -365,7 +365,7 @@ if ('onhashchange' in window) { 304 68/push "Ab"/imm32 305 68/push _test-tmp-stream/imm32 306 # . . call -307 e8/call check-stream-equal/disp32 +307 e8/call check-stream-equal/disp32 308 # . . discard args 309 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 310 # end @@ -425,7 +425,7 @@ if ('onhashchange' in window) { 364 68/push "CD"/imm32 365 68/push _test-tmp-stream/imm32 366 # . . call -367 e8/call check-stream-equal/disp32 +367 e8/call check-stream-equal/disp32 368 # . . discard args 369 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 370 # end diff --git a/html/subx/061read-byte.subx.html b/html/subx/061read-byte.subx.html index c12ec991..4071cc5f 100644 --- a/html/subx/061read-byte.subx.html +++ b/html/subx/061read-byte.subx.html @@ -3,8 +3,8 @@ Mu - subx/061read-byte.subx - - + + @@ -14,18 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.CommentedCode { color: #8a8a8a; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxTest { color: #5f8700; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -42,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -94,274 +93,266 @@ if ('onhashchange' in window) { 31 # . op subop mod rm32 base index scale r32 32 # . 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 33 - 34 #? Entry: # run a single test, while debugging - 35 #? e8/call test-read-byte-buffered-multiple/disp32 - 36 #? e8/call test-read-byte-buffered-refills-buffer/disp32 - 37 #? # syscall(exit, Num-test-failures) - 38 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 39 #? b8/copy-to-EAX 1/imm32/exit - 40 #? cd/syscall 0x80/imm8 - 41 - 42 # return next byte value in EAX, with top 3 bytes cleared. - 43 # On reaching end of file, return 0xffffffff (Eof). - 44 read-byte-buffered: # f : (address buffered-file) -> byte-or-Eof/EAX - 45 # . prolog - 46 55/push-EBP - 47 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 48 # . save registers - 49 51/push-ECX - 50 56/push-ESI - 51 # ESI = f - 52 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - 53 # ECX = f->read - 54 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX - 55 # if (f->read >= f->write) populate stream from file - 56 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) - 57 7c/jump-if-lesser $read-byte-buffered:from-stream/disp8 - 58 # . clear-stream(stream = f+4) - 59 # . . push args - 60 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX - 61 50/push-EAX - 62 # . . call - 63 e8/call clear-stream/disp32 - 64 # . . discard args - 65 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 66 # . f->read must now be 0; update its cache at ECX - 67 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - 68 # . EAX = read(f->fd, stream = f+4) - 69 # . . push args - 70 50/push-EAX - 71 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - 72 # . . call - 73 e8/call read/disp32 - 74 # . . discard args - 75 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 76 # if (EAX == 0) return 0xffffffff - 77 3d/compare-EAX-and 0/imm32 - 78 75/jump-if-not-equal $read-byte-buffered:from-stream/disp8 - 79 b8/copy-to-EAX 0xffffffff/imm32/Eof - 80 eb/jump $read-byte-buffered:end/disp8 - 81 $read-byte-buffered:from-stream: - 82 # read byte from stream - 83 # AL = f->data[f->read] - 84 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 85 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy byte at *(ESI+ECX+16) to AL - 86 # ++f->read - 87 ff 0/subop/increment 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # increment *(ESI+8) - 88 $read-byte-buffered:end: - 89 # . restore registers - 90 5e/pop-to-ESI - 91 59/pop-to-ECX - 92 # . epilog - 93 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 94 5d/pop-to-EBP - 95 c3/return - 96 - 97 # - tests - 98 - 99 test-read-byte-buffered-single: -100 # - check that read-byte-buffered returns first byte of 'file' -101 # setup -102 # . clear-stream(_test-stream) -103 # . . push args -104 68/push _test-stream/imm32 -105 # . . call -106 e8/call clear-stream/disp32 -107 # . . discard args -108 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -109 # . clear-stream(_test-buffered-file+4) -110 # . . push args -111 b8/copy-to-EAX _test-buffered-file/imm32 -112 05/add-to-EAX 4/imm32 -113 50/push-EAX + 34 # return next byte value in EAX, with top 3 bytes cleared. + 35 # On reaching end of file, return 0xffffffff (Eof). + 36 read-byte-buffered: # f : (address buffered-file) -> byte-or-Eof/EAX + 37 # . prolog + 38 55/push-EBP + 39 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 40 # . save registers + 41 51/push-ECX + 42 56/push-ESI + 43 # ESI = f + 44 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 45 # ECX = f->read + 46 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX + 47 # if (f->read >= f->write) populate stream from file + 48 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) + 49 7c/jump-if-lesser $read-byte-buffered:from-stream/disp8 + 50 # . clear-stream(stream = f+4) + 51 # . . push args + 52 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX + 53 50/push-EAX + 54 # . . call + 55 e8/call clear-stream/disp32 + 56 # . . discard args + 57 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 58 # . f->read must now be 0; update its cache at ECX + 59 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX + 60 # . EAX = read(f->fd, stream = f+4) + 61 # . . push args + 62 50/push-EAX + 63 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI + 64 # . . call + 65 e8/call read/disp32 + 66 # . . discard args + 67 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 68 # if (EAX == 0) return 0xffffffff + 69 3d/compare-EAX-and 0/imm32 + 70 75/jump-if-not-equal $read-byte-buffered:from-stream/disp8 + 71 b8/copy-to-EAX 0xffffffff/imm32/Eof + 72 eb/jump $read-byte-buffered:end/disp8 + 73 $read-byte-buffered:from-stream: + 74 # read byte from stream + 75 # AL = f->data[f->read] + 76 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 77 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy byte at *(ESI+ECX+16) to AL + 78 # ++f->read + 79 ff 0/subop/increment 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # increment *(ESI+8) + 80 $read-byte-buffered:end: + 81 # . restore registers + 82 5e/pop-to-ESI + 83 59/pop-to-ECX + 84 # . epilog + 85 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 86 5d/pop-to-EBP + 87 c3/return + 88 + 89 # - tests + 90 + 91 test-read-byte-buffered-single: + 92 # - check that read-byte-buffered returns first byte of 'file' + 93 # setup + 94 # . clear-stream(_test-stream) + 95 # . . push args + 96 68/push _test-stream/imm32 + 97 # . . call + 98 e8/call clear-stream/disp32 + 99 # . . discard args +100 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +101 # . clear-stream(_test-buffered-file+4) +102 # . . push args +103 b8/copy-to-EAX _test-buffered-file/imm32 +104 05/add-to-EAX 4/imm32 +105 50/push-EAX +106 # . . call +107 e8/call clear-stream/disp32 +108 # . . discard args +109 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +110 # . write(_test-stream, "Ab") +111 # . . push args +112 68/push "Ab"/imm32 +113 68/push _test-stream/imm32 114 # . . call -115 e8/call clear-stream/disp32 +115 e8/call write/disp32 116 # . . discard args -117 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -118 # . write(_test-stream, "Ab") +117 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +118 # read-byte-buffered(_test-buffered-file) 119 # . . push args -120 68/push "Ab"/imm32 -121 68/push _test-stream/imm32 -122 # . . call -123 e8/call write/disp32 -124 # . . discard args -125 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -126 # read-byte-buffered(_test-buffered-file) -127 # . . push args -128 68/push _test-buffered-file/imm32 -129 # . . call -130 e8/call read-byte-buffered/disp32 -131 # . . discard args -132 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -133 # check-ints-equal(EAX, 'A', msg) -134 # . . push args -135 68/push "F - test-read-byte-buffered-single"/imm32 -136 68/push 0x41/imm32 -137 50/push-EAX -138 # . . call -139 e8/call check-ints-equal/disp32 -140 # . . discard args -141 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -142 # . end -143 c3/return -144 -145 test-read-byte-buffered-multiple: -146 # - call read-byte-buffered twice, check that second call returns second byte -147 # setup -148 # . clear-stream(_test-stream) -149 # . . push args -150 68/push _test-stream/imm32 -151 # . . call -152 e8/call clear-stream/disp32 -153 # . . discard args -154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -155 # . clear-stream(_test-buffered-file+4) -156 # . . push args -157 b8/copy-to-EAX _test-buffered-file/imm32 -158 05/add-to-EAX 4/imm32 -159 50/push-EAX +120 68/push _test-buffered-file/imm32 +121 # . . call +122 e8/call read-byte-buffered/disp32 +123 # . . discard args +124 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +125 # check-ints-equal(EAX, 'A', msg) +126 # . . push args +127 68/push "F - test-read-byte-buffered-single"/imm32 +128 68/push 0x41/imm32 +129 50/push-EAX +130 # . . call +131 e8/call check-ints-equal/disp32 +132 # . . discard args +133 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +134 # . end +135 c3/return +136 +137 test-read-byte-buffered-multiple: +138 # - call read-byte-buffered twice, check that second call returns second byte +139 # setup +140 # . clear-stream(_test-stream) +141 # . . push args +142 68/push _test-stream/imm32 +143 # . . call +144 e8/call clear-stream/disp32 +145 # . . discard args +146 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +147 # . clear-stream(_test-buffered-file+4) +148 # . . push args +149 b8/copy-to-EAX _test-buffered-file/imm32 +150 05/add-to-EAX 4/imm32 +151 50/push-EAX +152 # . . call +153 e8/call clear-stream/disp32 +154 # . . discard args +155 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +156 # . write(_test-stream, "Ab") +157 # . . push args +158 68/push "Ab"/imm32 +159 68/push _test-stream/imm32 160 # . . call -161 e8/call clear-stream/disp32 +161 e8/call write/disp32 162 # . . discard args -163 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -164 # . write(_test-stream, "Ab") +163 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +164 # read-byte-buffered(_test-buffered-file) 165 # . . push args -166 68/push "Ab"/imm32 -167 68/push _test-stream/imm32 -168 # . . call -169 e8/call write/disp32 -170 # . . discard args -171 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -172 # read-byte-buffered(_test-buffered-file) -173 # . . push args -174 68/push _test-buffered-file/imm32 -175 # . . call -176 e8/call read-byte-buffered/disp32 -177 # . . discard args -178 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -179 # read-byte-buffered(_test-buffered-file) -180 # . . push args -181 68/push _test-buffered-file/imm32 -182 # . . call -183 e8/call read-byte-buffered/disp32 -184 # . . discard args -185 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -186 # check-ints-equal(EAX, 'b', msg) -187 # . . push args -188 68/push "F - test-read-byte-buffered-multiple"/imm32 -189 68/push 0x62/imm32 -190 50/push-EAX -191 # . . call -192 e8/call check-ints-equal/disp32 -193 # . . discard args -194 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -195 # . end -196 c3/return -197 -198 test-read-byte-buffered-end-of-file: -199 # - call read-byte-buffered on an empty 'file', check that it returns Eof -200 # setup -201 # . clear-stream(_test-stream) -202 # . . push args -203 68/push _test-stream/imm32 -204 # . . call -205 e8/call clear-stream/disp32 -206 # . . discard args -207 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -208 # . clear-stream(_test-buffered-file+4) -209 # . . push args -210 b8/copy-to-EAX _test-buffered-file/imm32 -211 05/add-to-EAX 4/imm32 -212 50/push-EAX -213 # . . call -214 e8/call clear-stream/disp32 -215 # . . discard args -216 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -217 # read-byte-buffered(_test-buffered-file) -218 # . . push args -219 68/push _test-buffered-file/imm32 -220 # . . call -221 e8/call read-byte-buffered/disp32 -222 # . . discard args -223 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -224 # check-ints-equal(EAX, 0xffffffff, msg) -225 # . . push args -226 68/push "F - test-read-byte-buffered-end-of-file"/imm32 -227 68/push 0xffffffff/imm32/Eof -228 50/push-EAX -229 # . . call -230 e8/call check-ints-equal/disp32 -231 # . . discard args -232 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -233 # . end -234 c3/return -235 -236 test-read-byte-buffered-refills-buffer: -237 # - consume buffered-file's buffer, check that next read-byte-buffered still works -238 # setup -239 # . clear-stream(_test-stream) -240 # . . push args -241 68/push _test-stream/imm32 -242 # . . call -243 e8/call clear-stream/disp32 -244 # . . discard args -245 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -246 # . clear-stream(_test-buffered-file+4) -247 # . . push args -248 b8/copy-to-EAX _test-buffered-file/imm32 -249 05/add-to-EAX 4/imm32 -250 50/push-EAX +166 68/push _test-buffered-file/imm32 +167 # . . call +168 e8/call read-byte-buffered/disp32 +169 # . . discard args +170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +171 # read-byte-buffered(_test-buffered-file) +172 # . . push args +173 68/push _test-buffered-file/imm32 +174 # . . call +175 e8/call read-byte-buffered/disp32 +176 # . . discard args +177 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +178 # check-ints-equal(EAX, 'b', msg) +179 # . . push args +180 68/push "F - test-read-byte-buffered-multiple"/imm32 +181 68/push 0x62/imm32 +182 50/push-EAX +183 # . . call +184 e8/call check-ints-equal/disp32 +185 # . . discard args +186 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +187 # . end +188 c3/return +189 +190 test-read-byte-buffered-end-of-file: +191 # - call read-byte-buffered on an empty 'file', check that it returns Eof +192 # setup +193 # . clear-stream(_test-stream) +194 # . . push args +195 68/push _test-stream/imm32 +196 # . . call +197 e8/call clear-stream/disp32 +198 # . . discard args +199 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +200 # . clear-stream(_test-buffered-file+4) +201 # . . push args +202 b8/copy-to-EAX _test-buffered-file/imm32 +203 05/add-to-EAX 4/imm32 +204 50/push-EAX +205 # . . call +206 e8/call clear-stream/disp32 +207 # . . discard args +208 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +209 # read-byte-buffered(_test-buffered-file) +210 # . . push args +211 68/push _test-buffered-file/imm32 +212 # . . call +213 e8/call read-byte-buffered/disp32 +214 # . . discard args +215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +216 # check-ints-equal(EAX, 0xffffffff, msg) +217 # . . push args +218 68/push "F - test-read-byte-buffered-end-of-file"/imm32 +219 68/push 0xffffffff/imm32/Eof +220 50/push-EAX +221 # . . call +222 e8/call check-ints-equal/disp32 +223 # . . discard args +224 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +225 # . end +226 c3/return +227 +228 test-read-byte-buffered-refills-buffer: +229 # - consume buffered-file's buffer, check that next read-byte-buffered still works +230 # setup +231 # . clear-stream(_test-stream) +232 # . . push args +233 68/push _test-stream/imm32 +234 # . . call +235 e8/call clear-stream/disp32 +236 # . . discard args +237 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +238 # . clear-stream(_test-buffered-file+4) +239 # . . push args +240 b8/copy-to-EAX _test-buffered-file/imm32 +241 05/add-to-EAX 4/imm32 +242 50/push-EAX +243 # . . call +244 e8/call clear-stream/disp32 +245 # . . discard args +246 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +247 # . write(_test-stream, "Abcdefgh") +248 # . . push args +249 68/push "Abcdefgh"/imm32 +250 68/push _test-stream/imm32 251 # . . call -252 e8/call clear-stream/disp32 +252 e8/call write/disp32 253 # . . discard args -254 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -255 # . write(_test-stream, "Abcdefgh") -256 # . . push args -257 68/push "Abcdefgh"/imm32 -258 68/push _test-stream/imm32 -259 # . . call -260 e8/call write/disp32 -261 # . . discard args -262 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -263 # pretend buffer is full -264 # . _test-buffered-file->read = 6 # >= _test-buffered-file->length -265 b8/copy-to-EAX _test-buffered-file/imm32 -266 c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 6/imm32 # copy to *(EAX+8) -267 # read-byte-buffered(_test-buffered-file) -268 # . . push args -269 68/push _test-buffered-file/imm32 -270 # . . call -271 e8/call read-byte-buffered/disp32 -272 # . . discard args -273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -274 # check-ints-equal(EAX, 'A', msg) -275 # . . push args -276 68/push "F - test-read-byte-buffered-refills-buffer"/imm32 -277 68/push 0x41/imm32 -278 50/push-EAX -279 # . . call -280 e8/call check-ints-equal/disp32 -281 # . . discard args -282 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -283 # . end -284 c3/return -285 -286 == data -287 -288 # a test buffered file for _test-stream -289 _test-buffered-file: -290 # file descriptor or (address stream) -291 _test-stream/imm32 -292 # current write index -293 0/imm32 -294 # current read index -295 0/imm32 -296 # length -297 6/imm32 -298 # data -299 00 00 00 00 00 00 # 6 bytes -300 -301 # . . vim:nowrap:textwidth=0 +254 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +255 # pretend buffer is full +256 # . _test-buffered-file->read = 6 # >= _test-buffered-file->length +257 b8/copy-to-EAX _test-buffered-file/imm32 +258 c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 6/imm32 # copy to *(EAX+8) +259 # read-byte-buffered(_test-buffered-file) +260 # . . push args +261 68/push _test-buffered-file/imm32 +262 # . . call +263 e8/call read-byte-buffered/disp32 +264 # . . discard args +265 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +266 # check-ints-equal(EAX, 'A', msg) +267 # . . push args +268 68/push "F - test-read-byte-buffered-refills-buffer"/imm32 +269 68/push 0x41/imm32 +270 50/push-EAX +271 # . . call +272 e8/call check-ints-equal/disp32 +273 # . . discard args +274 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +275 # . end +276 c3/return +277 +278 == data +279 +280 # a test buffered file for _test-stream +281 _test-buffered-file: +282 # file descriptor or (address stream) +283 _test-stream/imm32 +284 # current write index +285 0/imm32 +286 # current read index +287 0/imm32 +288 # length +289 6/imm32 +290 # data +291 00 00 00 00 00 00 # 6 bytes +292 +293 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/062write-stream.subx.html b/html/subx/062write-stream.subx.html index 1e97f935..f7b05f1e 100644 --- a/html/subx/062write-stream.subx.html +++ b/html/subx/062write-stream.subx.html @@ -3,8 +3,8 @@ Mu - subx/062write-stream.subx - - + + @@ -14,16 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } -.subxS1Comment { color: #0000af; } .CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } .subxTest { color: #5f8700; } -.Constant { color: #008787; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -119,7 +119,7 @@ if ('onhashchange' in window) { 58 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX 59 50/push-EAX 60 # . . call - 61 e8/call _append-4/disp32 + 61 e8/call _append-4/disp32 62 # . . discard args 63 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP 64 # f->write += EAX @@ -233,7 +233,7 @@ if ('onhashchange' in window) { 172 68/push "Ab"/imm32 173 68/push _test-stream/imm32 174 # . . call -175 e8/call check-stream-equal/disp32 +175 e8/call check-stream-equal/disp32 176 # . . discard args 177 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 178 # . end @@ -295,7 +295,7 @@ if ('onhashchange' in window) { 234 68/push "CD"/imm32 235 68/push _test-stream/imm32 236 # . . call -237 e8/call check-stream-equal/disp32 +237 e8/call check-stream-equal/disp32 238 # . . discard args 239 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 240 # . end diff --git a/html/subx/063error.subx.html b/html/subx/063error.subx.html index 96fd3c44..94b56159 100644 --- a/html/subx/063error.subx.html +++ b/html/subx/063error.subx.html @@ -3,8 +3,8 @@ Mu - subx/063error.subx - - + + @@ -15,13 +15,13 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxFunction { color: #af5f00; text-decoration: underline; } .Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxS2Comment { color: #8a8a8a; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -98,7 +98,7 @@ if ('onhashchange' in window) { 39 68/push 1/imm32 40 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 41 # . . call -42 e8/call stop/disp32 +42 e8/call stop/disp32 43 # should never get past this point 44 $error:dead-end: 45 # . epilog diff --git a/html/subx/064write-byte.subx.html b/html/subx/064write-byte.subx.html index 7ba45da6..f272e0bd 100644 --- a/html/subx/064write-byte.subx.html +++ b/html/subx/064write-byte.subx.html @@ -3,8 +3,8 @@ Mu - subx/064write-byte.subx - - + + @@ -14,16 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.subxFunction { color: #af5f00; text-decoration: underline; } .subxTest { color: #5f8700; } -.Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } .subxH1Comment { color: #005faf; text-decoration: underline; } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -40,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -176,7 +176,7 @@ if ('onhashchange' in window) { 115 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 116 # . clear-stream(_test-buffered-file+4) 117 # . . push args -118 b8/copy-to-EAX _test-buffered-file/imm32 +118 b8/copy-to-EAX _test-buffered-file/imm32 119 05/add-to-EAX 4/imm32 120 50/push-EAX 121 # . . call @@ -186,14 +186,14 @@ if ('onhashchange' in window) { 125 # write-byte-buffered(_test-buffered-file, 'A') 126 # . . push args 127 68/push 0x41/imm32 -128 68/push _test-buffered-file/imm32 +128 68/push _test-buffered-file/imm32 129 # . . call 130 e8/call write-byte-buffered/disp32 131 # . . discard args 132 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 133 # flush(_test-buffered-file) 134 # . . push args -135 68/push _test-buffered-file/imm32 +135 68/push _test-buffered-file/imm32 136 # . . call 137 e8/call flush/disp32 138 # . . discard args @@ -204,7 +204,7 @@ if ('onhashchange' in window) { 143 68/push "A"/imm32 144 68/push _test-stream/imm32 145 # . . call -146 e8/call check-stream-equal/disp32 +146 e8/call check-stream-equal/disp32 147 # . . discard args 148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 149 # . end @@ -222,7 +222,7 @@ if ('onhashchange' in window) { 161 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 162 # . clear-stream(_test-buffered-file+4) 163 # . . push args -164 b8/copy-to-EAX _test-buffered-file/imm32 +164 b8/copy-to-EAX _test-buffered-file/imm32 165 05/add-to-EAX 4/imm32 166 50/push-EAX 167 # . . call @@ -233,7 +233,7 @@ if ('onhashchange' in window) { 172 # . write(_test-buffered-file+4, 'abcdef') 173 # . . push args 174 68/push "abcdef"/imm32 -175 b8/copy-to-EAX _test-buffered-file/imm32 +175 b8/copy-to-EAX _test-buffered-file/imm32 176 05/add-to-EAX 4/imm32 177 50/push-EAX 178 # . . call @@ -243,14 +243,14 @@ if ('onhashchange' in window) { 182 # write-byte-buffered(_test-buffered-file, 'g') 183 # . . push args 184 68/push 0x67/imm32 -185 68/push _test-buffered-file/imm32 +185 68/push _test-buffered-file/imm32 186 # . . call 187 e8/call write-byte-buffered/disp32 188 # . . discard args 189 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 190 # flush(_test-buffered-file) 191 # . . push args -192 68/push _test-buffered-file/imm32 +192 68/push _test-buffered-file/imm32 193 # . . call 194 e8/call flush/disp32 195 # . . discard args @@ -261,7 +261,7 @@ if ('onhashchange' in window) { 200 68/push "abcdefg"/imm32 201 68/push _test-stream/imm32 202 # . . call -203 e8/call check-stream-equal/disp32 +203 e8/call check-stream-equal/disp32 204 # . . discard args 205 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 206 # . end @@ -340,7 +340,7 @@ if ('onhashchange' in window) { 279 68/push "A"/imm32 280 68/push _test-stream/imm32 281 # . . call -282 e8/call check-stream-equal/disp32 +282 e8/call check-stream-equal/disp32 283 # . . discard args 284 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 285 # . end diff --git a/html/subx/065hex.subx.html b/html/subx/065hex.subx.html index 19119cac..75377786 100644 --- a/html/subx/065hex.subx.html +++ b/html/subx/065hex.subx.html @@ -3,8 +3,8 @@ Mu - subx/065hex.subx - - + + @@ -15,13 +15,12 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } .subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.LineNr { } .subxTest { color: #5f8700; } -.subxMinorFunction { color: #875f5f; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } .subxS2Comment { color: #8a8a8a; } --> @@ -39,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -83,7 +82,7 @@ if ('onhashchange' in window) { 23 # if s is empty return false 24 b8/copy-to-EAX 0/imm32/false 25 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 26 7d/jump-if-greater-or-equal $is-hex-int?:end/disp8 + 26 73/jump-if-greater-or-equal-unsigned $is-hex-int?:end/disp8 27 # skip past leading '-' 28 # . if (*curr == '-') ++curr 29 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX @@ -104,7 +103,7 @@ if ('onhashchange' in window) { 44 $is-hex-int?:initial-0x: 45 # . if (curr >= in->end) return true 46 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 47 7d/jump-if-greater-or-equal $is-hex-int?:true/disp8 + 47 73/jump-if-greater-or-equal-unsigned $is-hex-int?:true/disp8 48 # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again 49 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX 50 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL @@ -115,13 +114,13 @@ if ('onhashchange' in window) { 55 $is-hex-int?:loop: 56 # if (curr >= in->end) return true 57 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 58 7d/jump-if-greater-or-equal $is-hex-int?:true/disp8 + 58 73/jump-if-greater-or-equal-unsigned $is-hex-int?:true/disp8 59 # EAX = is-hex-digit?(*curr) 60 # . . push args 61 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL 62 50/push-EAX 63 # . . call - 64 e8/call is-hex-digit?/disp32 + 64 e8/call is-hex-digit?/disp32 65 # . . discard args 66 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 67 # if (EAX == false) return false @@ -148,718 +147,737 @@ if ('onhashchange' in window) { 88 # . prolog 89 55/push-EBP 90 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 91 # var slice/ECX = "34" - 92 68/push _test-slice-hex-int-end/imm32 - 93 68/push _test-slice-hex-int/imm32 - 94 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 95 # EAX = is-hex-int?(slice) - 96 # . . push args + 91 # (EAX..ECX) = "34" + 92 b8/copy-to-EAX "34"/imm32 + 93 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 94 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 95 05/add-to-EAX 4/imm32 + 96 # var slice/ECX = {EAX, ECX} 97 51/push-ECX - 98 # . . call - 99 e8/call is-hex-int?/disp32 -100 # . . discard args -101 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -102 # check-ints-equal(EAX, 1, msg) -103 # . . push args -104 68/push "F - test-is-hex-int"/imm32 -105 68/push 1/imm32/true -106 50/push-EAX -107 # . . call -108 e8/call check-ints-equal/disp32 -109 # . . discard args -110 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -111 # . epilog -112 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -113 5d/pop-to-EBP -114 c3/return -115 -116 test-is-hex-int-handles-letters: -117 # . prolog -118 55/push-EBP -119 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -120 # var slice/ECX = "34a" -121 68/push _test-slice-hex-int-letters-end/imm32 -122 68/push _test-slice-hex-int-letters/imm32 -123 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -124 # EAX = is-hex-int?(slice) -125 # . . push args -126 51/push-ECX -127 # . . call -128 e8/call is-hex-int?/disp32 -129 # . . discard args -130 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -131 # check-ints-equal(EAX, 1, msg) -132 # . . push args -133 68/push "F - test-is-hex-int-handles-letters"/imm32 -134 68/push 1/imm32/true -135 50/push-EAX -136 # . . call -137 e8/call check-ints-equal/disp32 -138 # . . discard args -139 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -140 # . epilog -141 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -142 5d/pop-to-EBP -143 c3/return -144 -145 test-is-hex-int-with-trailing-char: -146 # . prolog -147 55/push-EBP -148 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -149 # var slice/ECX = "34q" -150 68/push _test-slice-digits-and-char-end/imm32 -151 68/push _test-slice-digits-and-char/imm32 -152 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -153 # EAX = is-hex-int?(slice) -154 # . . push args -155 51/push-ECX -156 # . . call -157 e8/call is-hex-int?/disp32 -158 # . . discard args -159 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -160 # check-ints-equal(EAX, 0, msg) -161 # . . push args -162 68/push "F - test-is-hex-int-with-trailing-char"/imm32 -163 68/push 0/imm32/false -164 50/push-EAX -165 # . . call -166 e8/call check-ints-equal/disp32 -167 # . . discard args -168 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -169 # . epilog -170 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -171 5d/pop-to-EBP -172 c3/return -173 -174 test-is-hex-int-with-leading-char: -175 # . prolog -176 55/push-EBP -177 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -178 # var slice/ECX = "q34" -179 68/push _test-slice-char-and-digits-end/imm32 -180 68/push _test-slice-char-and-digits/imm32 -181 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -182 # EAX = is-hex-int?(slice) -183 # . . push args -184 51/push-ECX -185 # . . call -186 e8/call is-hex-int?/disp32 -187 # . . discard args -188 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -189 # check-ints-equal(EAX, 0, msg) -190 # . . push args -191 68/push "F - test-is-hex-int-with-leading-char"/imm32 -192 68/push 0/imm32/false -193 50/push-EAX -194 # . . call -195 e8/call check-ints-equal/disp32 -196 # . . discard args -197 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -198 # . epilog -199 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -200 5d/pop-to-EBP -201 c3/return -202 -203 test-is-hex-int-empty: -204 # . prolog -205 55/push-EBP -206 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -207 # var slice/ECX = "" -208 68/push _test-slice-empty-end/imm32 -209 68/push _test-slice-empty/imm32 -210 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -211 # EAX = is-hex-int?(slice) -212 # . . push args -213 51/push-ECX + 98 50/push-EAX + 99 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +100 # EAX = is-hex-int?(slice) +101 # . . push args +102 51/push-ECX +103 # . . call +104 e8/call is-hex-int?/disp32 +105 # . . discard args +106 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +107 # check-ints-equal(EAX, 1, msg) +108 # . . push args +109 68/push "F - test-is-hex-int"/imm32 +110 68/push 1/imm32/true +111 50/push-EAX +112 # . . call +113 e8/call check-ints-equal/disp32 +114 # . . discard args +115 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +116 # . epilog +117 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +118 5d/pop-to-EBP +119 c3/return +120 +121 test-is-hex-int-handles-letters: +122 # . prolog +123 55/push-EBP +124 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +125 # (EAX..ECX) = "34a" +126 b8/copy-to-EAX "34a"/imm32 +127 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +128 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +129 05/add-to-EAX 4/imm32 +130 # var slice/ECX = {EAX, ECX} +131 51/push-ECX +132 50/push-EAX +133 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +134 # EAX = is-hex-int?(slice) +135 # . . push args +136 51/push-ECX +137 # . . call +138 e8/call is-hex-int?/disp32 +139 # . . discard args +140 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +141 # check-ints-equal(EAX, 1, msg) +142 # . . push args +143 68/push "F - test-is-hex-int-handles-letters"/imm32 +144 68/push 1/imm32/true +145 50/push-EAX +146 # . . call +147 e8/call check-ints-equal/disp32 +148 # . . discard args +149 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +150 # . epilog +151 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +152 5d/pop-to-EBP +153 c3/return +154 +155 test-is-hex-int-with-trailing-char: +156 # . prolog +157 55/push-EBP +158 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +159 # (EAX..ECX) = "34q" +160 b8/copy-to-EAX "34q"/imm32 +161 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +162 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +163 05/add-to-EAX 4/imm32 +164 # var slice/ECX = {EAX, ECX} +165 51/push-ECX +166 50/push-EAX +167 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +168 # EAX = is-hex-int?(slice) +169 # . . push args +170 51/push-ECX +171 # . . call +172 e8/call is-hex-int?/disp32 +173 # . . discard args +174 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +175 # check-ints-equal(EAX, 0, msg) +176 # . . push args +177 68/push "F - test-is-hex-int-with-trailing-char"/imm32 +178 68/push 0/imm32/false +179 50/push-EAX +180 # . . call +181 e8/call check-ints-equal/disp32 +182 # . . discard args +183 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +184 # . epilog +185 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +186 5d/pop-to-EBP +187 c3/return +188 +189 test-is-hex-int-with-leading-char: +190 # . prolog +191 55/push-EBP +192 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +193 # (EAX..ECX) = "q34" +194 b8/copy-to-EAX "q34"/imm32 +195 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +196 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +197 05/add-to-EAX 4/imm32 +198 # var slice/ECX = {EAX, ECX} +199 51/push-ECX +200 50/push-EAX +201 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +202 # EAX = is-hex-int?(slice) +203 # . . push args +204 51/push-ECX +205 # . . call +206 e8/call is-hex-int?/disp32 +207 # . . discard args +208 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +209 # check-ints-equal(EAX, 0, msg) +210 # . . push args +211 68/push "F - test-is-hex-int-with-leading-char"/imm32 +212 68/push 0/imm32/false +213 50/push-EAX 214 # . . call -215 e8/call is-hex-int?/disp32 +215 e8/call check-ints-equal/disp32 216 # . . discard args -217 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -218 # check-ints-equal(EAX, 0, msg) -219 # . . push args -220 68/push "F - test-is-hex-int-empty"/imm32 -221 68/push 0/imm32/false -222 50/push-EAX -223 # . . call -224 e8/call check-ints-equal/disp32 -225 # . . discard args -226 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -227 # . epilog -228 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -229 5d/pop-to-EBP -230 c3/return -231 -232 test-is-hex-int-handles-0x-prefix: -233 # . prolog -234 55/push-EBP -235 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -236 # var slice/ECX = "0x3a" -237 68/push _test-slice-hex-int-with-0x-prefix-end/imm32 -238 68/push _test-slice-hex-int-with-0x-prefix/imm32 -239 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -240 # EAX = is-hex-int?(slice) -241 # . . push args -242 51/push-ECX +217 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +218 # . epilog +219 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +220 5d/pop-to-EBP +221 c3/return +222 +223 test-is-hex-int-empty: +224 # . prolog +225 55/push-EBP +226 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +227 # var slice/ECX = "" +228 68/push 0/imm32 +229 68/push 0/imm32 +230 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +231 # EAX = is-hex-int?(slice) +232 # . . push args +233 51/push-ECX +234 # . . call +235 e8/call is-hex-int?/disp32 +236 # . . discard args +237 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +238 # check-ints-equal(EAX, 0, msg) +239 # . . push args +240 68/push "F - test-is-hex-int-empty"/imm32 +241 68/push 0/imm32/false +242 50/push-EAX 243 # . . call -244 e8/call is-hex-int?/disp32 +244 e8/call check-ints-equal/disp32 245 # . . discard args -246 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -247 # check-ints-equal(EAX, 1, msg) -248 # . . push args -249 68/push "F - test-is-hex-int-handles-0x-prefix"/imm32 -250 68/push 1/imm32/true -251 50/push-EAX -252 # . . call -253 e8/call check-ints-equal/disp32 -254 # . . discard args -255 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -256 # . epilog -257 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -258 5d/pop-to-EBP -259 c3/return -260 -261 test-is-hex-int-handles-negative: -262 # . prolog -263 55/push-EBP -264 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -265 # var slice/ECX = "-34a" -266 68/push _test-slice-hex-int-letters-end/imm32 -267 68/push _test-slice-hex-int-letters-negative/imm32 -268 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -269 # EAX = is-hex-int?(slice) -270 # . . push args -271 51/push-ECX -272 # . . call -273 e8/call is-hex-int?/disp32 -274 # . . discard args -275 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -276 # check-ints-equal(EAX, 1, msg) -277 # . . push args -278 68/push "F - test-is-hex-int-handles-negative"/imm32 -279 68/push 1/imm32/true -280 50/push-EAX -281 # . . call -282 e8/call check-ints-equal/disp32 -283 # . . discard args -284 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -285 # . epilog -286 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -287 5d/pop-to-EBP -288 c3/return -289 -290 test-is-hex-int-handles-negative-0x-prefix: -291 # . prolog -292 55/push-EBP -293 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -294 # var slice/ECX = "-0x3a" -295 68/push _test-slice-hex-int-with-0x-prefix-end/imm32 -296 68/push _test-slice-hex-int-with-0x-prefix-negative/imm32 -297 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -298 # EAX = is-hex-int?(slice) -299 # . . push args -300 51/push-ECX -301 # . . call -302 e8/call is-hex-int?/disp32 -303 # . . discard args -304 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -305 # check-ints-equal(EAX, 1, msg) -306 # . . push args -307 68/push "F - test-is-hex-int-handles-negative-0x-prefix"/imm32 -308 68/push 1/imm32/true -309 50/push-EAX -310 # . . call -311 e8/call check-ints-equal/disp32 -312 # . . discard args -313 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -314 # . epilog -315 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -316 5d/pop-to-EBP -317 c3/return -318 -319 parse-hex-int: # in : (address slice) -> result/EAX -320 # . prolog -321 55/push-EBP -322 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -323 # . save registers -324 51/push-ECX -325 52/push-EDX -326 53/push-EBX -327 56/push-ESI -328 # result/EBX = 0 -329 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -330 # ECX = s -331 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -332 # EDX = s->end -333 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX -334 # curr/ECX = s->start -335 8b/copy 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX -336 # negate?/ESI = false -337 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI -338 $parse-hex-int:negative: -339 # . if (*curr == '-') negate = true -340 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -341 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -342 3d/compare-EAX-and 0x2d/imm32/- -343 75/jump-if-not-equal $parse-hex-int:initial-0/disp8 -344 # . ++curr -345 41/increment-ECX -346 # . negate = true -347 be/copy-to-ESI 1/imm32/true -348 $parse-hex-int:initial-0: -349 # skip past leading '0x' -350 # . if (*curr != '0') jump to loop -351 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -352 3d/compare-EAX-and 0x30/imm32/0 -353 75/jump-if-not-equal $parse-hex-int:loop/disp8 -354 # . ++curr -355 41/increment-ECX -356 $parse-hex-int:initial-0x: -357 # . if (curr >= in->end) return result -358 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -359 7d/jump-if-greater-or-equal $parse-hex-int:end/disp8 -360 # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again -361 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -362 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -363 3d/compare-EAX-and 0x78/imm32/x -364 75/jump-if-not-equal $parse-hex-int:loop/disp8 -365 # . ++curr -366 41/increment-ECX -367 $parse-hex-int:loop: -368 # if (curr >= in->end) break -369 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -370 7d/jump-if-greater-or-equal $parse-hex-int:negate/disp8 -371 # EAX = from-hex-char(*curr) -372 # . . copy arg to EAX -373 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -374 # . . call -375 e8/call from-hex-char/disp32 -376 # result = result * 16 + EAX -377 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 4/imm8 # shift EBX left by 4 bits -378 01/add 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # add EAX to EBX -379 # ++curr +246 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +247 # . epilog +248 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +249 5d/pop-to-EBP +250 c3/return +251 +252 test-is-hex-int-handles-0x-prefix: +253 # . prolog +254 55/push-EBP +255 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +256 # (EAX..ECX) = "0x3a" +257 b8/copy-to-EAX "0x3a"/imm32 +258 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +259 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +260 05/add-to-EAX 4/imm32 +261 # var slice/ECX = {EAX, ECX} +262 51/push-ECX +263 50/push-EAX +264 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +265 # EAX = is-hex-int?(slice) +266 # . . push args +267 51/push-ECX +268 # . . call +269 e8/call is-hex-int?/disp32 +270 # . . discard args +271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +272 # check-ints-equal(EAX, 1, msg) +273 # . . push args +274 68/push "F - test-is-hex-int-handles-0x-prefix"/imm32 +275 68/push 1/imm32/true +276 50/push-EAX +277 # . . call +278 e8/call check-ints-equal/disp32 +279 # . . discard args +280 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +281 # . epilog +282 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +283 5d/pop-to-EBP +284 c3/return +285 +286 test-is-hex-int-handles-negative: +287 # . prolog +288 55/push-EBP +289 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +290 # (EAX..ECX) = "-34a" +291 b8/copy-to-EAX "-34a"/imm32 +292 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +293 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +294 05/add-to-EAX 4/imm32 +295 # var slice/ECX = {EAX, ECX} +296 51/push-ECX +297 50/push-EAX +298 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +299 # EAX = is-hex-int?(slice) +300 # . . push args +301 51/push-ECX +302 # . . call +303 e8/call is-hex-int?/disp32 +304 # . . discard args +305 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +306 # check-ints-equal(EAX, 1, msg) +307 # . . push args +308 68/push "F - test-is-hex-int-handles-negative"/imm32 +309 68/push 1/imm32/true +310 50/push-EAX +311 # . . call +312 e8/call check-ints-equal/disp32 +313 # . . discard args +314 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +315 # . epilog +316 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +317 5d/pop-to-EBP +318 c3/return +319 +320 test-is-hex-int-handles-negative-0x-prefix: +321 # . prolog +322 55/push-EBP +323 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +324 # (EAX..ECX) = "-0x3a" +325 b8/copy-to-EAX "-0x3a"/imm32 +326 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +327 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +328 05/add-to-EAX 4/imm32 +329 # var slice/ECX = {EAX, ECX} +330 51/push-ECX +331 50/push-EAX +332 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +333 # EAX = is-hex-int?(slice) +334 # . . push args +335 51/push-ECX +336 # . . call +337 e8/call is-hex-int?/disp32 +338 # . . discard args +339 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +340 # check-ints-equal(EAX, 1, msg) +341 # . . push args +342 68/push "F - test-is-hex-int-handles-negative-0x-prefix"/imm32 +343 68/push 1/imm32/true +344 50/push-EAX +345 # . . call +346 e8/call check-ints-equal/disp32 +347 # . . discard args +348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +349 # . epilog +350 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +351 5d/pop-to-EBP +352 c3/return +353 +354 parse-hex-int: # in : (address slice) -> result/EAX +355 # . prolog +356 55/push-EBP +357 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +358 # . save registers +359 51/push-ECX +360 52/push-EDX +361 53/push-EBX +362 56/push-ESI +363 # result/EBX = 0 +364 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX +365 # ECX = s +366 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +367 # EDX = s->end +368 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX +369 # curr/ECX = s->start +370 8b/copy 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX +371 # negate?/ESI = false +372 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI +373 $parse-hex-int:negative: +374 # . if (*curr == '-') negate = true +375 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +376 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +377 3d/compare-EAX-and 0x2d/imm32/- +378 75/jump-if-not-equal $parse-hex-int:initial-0/disp8 +379 # . ++curr 380 41/increment-ECX -381 # loop -382 eb/jump $parse-hex-int:loop/disp8 -383 $parse-hex-int:negate: -384 81 7/subop/compare 3/mod/direct 6/rm32/ESI . . . . . 0/imm32 # compare ESI -385 74/jump-if-equal $parse-hex-int:end/disp8 -386 f7 3/subop/negate 3/mod/direct 3/rm32/EBX . . . . . . # negate EBX -387 $parse-hex-int:end: -388 89/copy 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to EAX -389 # . restore registers -390 5e/pop-to-ESI -391 5b/pop-to-EBX -392 5a/pop-to-EDX -393 59/pop-to-ECX -394 # . epilog -395 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -396 5d/pop-to-EBP -397 c3/return -398 -399 test-parse-hex-int-single-digit: -400 # . prolog -401 55/push-EBP -402 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -403 # var slice/ECX = "a" -404 68/push _test-slice-hex-int-single-letter-end/imm32 -405 68/push _test-slice-hex-int-single-letter/imm32 -406 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -407 # EAX = parse-hex-int(slice) -408 # . . push args -409 51/push-ECX -410 # . . call -411 e8/call parse-hex-int/disp32 -412 # . . discard args -413 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -414 # check-ints-equal(EAX, 0xa, msg) -415 # . . push args -416 68/push "F - test-parse-hex-int-single-digit"/imm32 -417 68/push 0xa/imm32 -418 50/push-EAX -419 # . . call -420 e8/call check-ints-equal/disp32 -421 # . . discard args -422 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -423 # . epilog -424 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -425 5d/pop-to-EBP -426 c3/return -427 -428 test-parse-hex-int-multi-digit: -429 # . prolog -430 55/push-EBP -431 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -432 # var slice/ECX = "34a" -433 68/push _test-slice-hex-int-letters-end/imm32 -434 68/push _test-slice-hex-int-letters/imm32 -435 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -436 # EAX = parse-hex-int(slice) -437 # . . push args -438 51/push-ECX -439 # . . call -440 e8/call parse-hex-int/disp32 -441 # . . discard args -442 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -443 # check-ints-equal(EAX, 0x34a, msg) -444 # . . push args -445 68/push "F - test-parse-hex-int-multi-digit"/imm32 -446 68/push 0x34a/imm32 -447 50/push-EAX -448 # . . call -449 e8/call check-ints-equal/disp32 -450 # . . discard args -451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -452 # . epilog -453 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -454 5d/pop-to-EBP -455 c3/return -456 -457 test-parse-hex-int-0x-prefix: -458 # . prolog -459 55/push-EBP -460 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -461 # var slice/ECX = "0x34" -462 68/push _test-slice-hex-int-with-0x-prefix-end/imm32 -463 68/push _test-slice-hex-int-with-0x-prefix/imm32 -464 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -465 # EAX = parse-hex-int(slice) -466 # . . push args -467 51/push-ECX -468 # . . call -469 e8/call parse-hex-int/disp32 -470 # . . discard args -471 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -472 # check-ints-equal(EAX, 0x34a, msg) -473 # . . push args -474 68/push "F - test-parse-hex-int-0x-prefix"/imm32 -475 68/push 0x34/imm32 -476 50/push-EAX -477 # . . call -478 e8/call check-ints-equal/disp32 -479 # . . discard args -480 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -481 # . epilog -482 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -483 5d/pop-to-EBP -484 c3/return -485 -486 test-parse-hex-int-zero: -487 # . prolog -488 55/push-EBP -489 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -490 # var slice/ECX = "0" -491 68/push _test-slice-hex-int-zero-end/imm32 -492 68/push _test-slice-hex-int-zero/imm32 -493 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -494 # EAX = parse-hex-int(slice) -495 # . . push args -496 51/push-ECX -497 # . . call -498 e8/call parse-hex-int/disp32 -499 # . . discard args -500 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -501 # check-ints-equal(EAX, 0x34a, msg) -502 # . . push args -503 68/push "F - test-parse-hex-int-zero"/imm32 -504 68/push 0/imm32 -505 50/push-EAX -506 # . . call -507 e8/call check-ints-equal/disp32 -508 # . . discard args -509 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -510 # . epilog -511 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -512 5d/pop-to-EBP -513 c3/return -514 -515 test-parse-hex-int-0-prefix: -516 # . prolog -517 55/push-EBP -518 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -519 # var slice/ECX = "03" -520 68/push _test-slice-hex-int-with-0-prefix-end/imm32 -521 68/push _test-slice-hex-int-with-0-prefix/imm32 -522 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -523 # EAX = parse-hex-int(slice) -524 # . . push args -525 51/push-ECX -526 # . . call -527 e8/call parse-hex-int/disp32 -528 # . . discard args -529 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -530 # check-ints-equal(EAX, 0x3, msg) -531 # . . push args -532 68/push "F - test-parse-hex-int-0-prefix"/imm32 -533 68/push 0x3/imm32 -534 50/push-EAX -535 # . . call -536 e8/call check-ints-equal/disp32 -537 # . . discard args -538 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -539 # . epilog -540 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -541 5d/pop-to-EBP -542 c3/return -543 -544 test-parse-hex-int-negative: -545 # . prolog -546 55/push-EBP -547 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -548 # var slice/ECX = "-03" -549 68/push _test-slice-hex-int-negative-with-0-prefix-end/imm32 -550 68/push _test-slice-hex-int-negative-with-0-prefix/imm32 -551 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -552 # EAX = parse-hex-int(slice) -553 # . . push args -554 51/push-ECX -555 # . . call -556 e8/call parse-hex-int/disp32 -557 # . . discard args -558 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -559 # check-ints-equal(EAX, 0xfffffffd, msg) -560 # . . push args -561 68/push "F - test-parse-hex-int-negative"/imm32 -562 68/push 0xfffffffd/imm32 -563 50/push-EAX -564 # . . call -565 e8/call check-ints-equal/disp32 -566 # . . discard args -567 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -568 # . epilog -569 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -570 5d/pop-to-EBP -571 c3/return -572 -573 is-hex-digit?: # c : byte -> EAX : boolean -574 # . prolog -575 55/push-EBP -576 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -577 # . save registers -578 51/push-ECX -579 # ECX = c -580 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -581 # return false if c < '0' -582 b8/copy-to-EAX 0/imm32/false -583 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x30/imm32 # compare ECX -584 7c/jump-if-lesser $is-hex-digit?:end/disp8 -585 # return false if c > 'f' -586 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x66/imm32 # compare ECX -587 7f/jump-if-greater $is-hex-digit?:end/disp8 -588 # return true if c <= '9' -589 b8/copy-to-EAX 1/imm32/true -590 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x39/imm32 # compare ECX -591 7e/jump-if-lesser-or-equal $is-hex-digit?:end/disp8 -592 # return true if c >= 'a' -593 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x61/imm32 # compare ECX -594 7d/jump-if-greater-or-equal $is-hex-digit?:end/disp8 -595 # otherwise return false -596 b8/copy-to-EAX 0/imm32/false -597 $is-hex-digit?:end: -598 # . restore registers -599 59/pop-to-ECX -600 # . epilog -601 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -602 5d/pop-to-EBP -603 c3/return -604 -605 test-hex-below-0: -606 # EAX = is-hex-digit?(0x2f) -607 # . . push args -608 68/push 0x2f/imm32 -609 # . . call -610 e8/call is-hex-digit?/disp32 -611 # . . discard args -612 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -613 # check-ints-equal(EAX, 0, msg) -614 # . . push args -615 68/push "F - test-hex-below-0"/imm32 -616 68/push 0/imm32/false -617 50/push-EAX -618 # . . call -619 e8/call check-ints-equal/disp32 -620 # . . discard args -621 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -622 c3/return -623 -624 test-hex-0-to-9: -625 # EAX = is-hex-digit?(0x30) -626 # . . push args -627 68/push 0x30/imm32 -628 # . . call -629 e8/call is-hex-digit?/disp32 -630 # . . discard args -631 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -632 # check-ints-equal(EAX, 1, msg) -633 # . . push args -634 68/push "F - test-hex-at-0"/imm32 -635 68/push 1/imm32/true -636 50/push-EAX -637 # . . call -638 e8/call check-ints-equal/disp32 -639 # . . discard args -640 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -641 # EAX = is-hex-digit?(0x39) -642 # . . push args -643 68/push 0x39/imm32 -644 # . . call -645 e8/call is-hex-digit?/disp32 -646 # . . discard args -647 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -648 # check-ints-equal(EAX, 1, msg) -649 # . . push args -650 68/push "F - test-hex-at-9"/imm32 -651 68/push 1/imm32/true -652 50/push-EAX -653 # . . call -654 e8/call check-ints-equal/disp32 -655 # . . discard args -656 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -657 c3/return -658 -659 test-hex-above-9-to-a: -660 # EAX = is-hex-digit?(0x3a) -661 # . . push args -662 68/push 0x3a/imm32 -663 # . . call -664 e8/call is-hex-digit?/disp32 -665 # . . discard args -666 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -667 # check-ints-equal(EAX, 0, msg) -668 # . . push args -669 68/push "F - test-hex-above-9-to-a"/imm32 -670 68/push 0/imm32/false -671 50/push-EAX -672 # . . call -673 e8/call check-ints-equal/disp32 -674 # . . discard args -675 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -676 c3/return -677 -678 test-hex-a-to-f: -679 # EAX = is-hex-digit?(0x61) -680 # . . push args -681 68/push 0x61/imm32 -682 # . . call -683 e8/call is-hex-digit?/disp32 -684 # . . discard args -685 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -686 # check-ints-equal(EAX, 1, msg) -687 # . . push args -688 68/push "F - test-hex-at-a"/imm32 -689 68/push 1/imm32/true -690 50/push-EAX -691 # . . call -692 e8/call check-ints-equal/disp32 -693 # . . discard args -694 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -695 # EAX = is-hex-digit?(0x66) -696 # . . push args -697 68/push 0x66/imm32 -698 # . . call -699 e8/call is-hex-digit?/disp32 -700 # . . discard args -701 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -702 # check-ints-equal(EAX, 1, msg) -703 # . . push args -704 68/push "F - test-hex-at-f"/imm32 -705 68/push 1/imm32/true -706 50/push-EAX -707 # . . call -708 e8/call check-ints-equal/disp32 -709 # . . discard args -710 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -711 c3/return -712 -713 test-hex-above-f: -714 # EAX = is-hex-digit?(0x67) -715 # . . push args -716 68/push 0x67/imm32 -717 # . . call -718 e8/call is-hex-digit?/disp32 -719 # . . discard args -720 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -721 # check-ints-equal(EAX, 0, msg) -722 # . . push args -723 68/push "F - test-hex-above-f"/imm32 -724 68/push 0/imm32/false -725 50/push-EAX -726 # . . call -727 e8/call check-ints-equal/disp32 -728 # . . discard args -729 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -730 c3/return -731 -732 from-hex-char: # in/EAX : byte -> out/EAX : num -733 # no error checking; accepts argument in EAX -734 # if (EAX <= '9') return EAX - '0' -735 3d/compare-EAX-with 0x39/imm32/9 -736 7f/jump-if-greater $from-hex-char:else/disp8 -737 2d/subtract-from-EAX 0x30/imm32/0 -738 c3/return -739 $from-hex-char:else: -740 # otherwise return EAX - 'a' + 10 -741 2d/subtract-from-EAX 0x57/imm32/a-10 -742 c3/return -743 -744 to-hex-char: # in/EAX : nibble -> out/EAX : byte -745 # no error checking; accepts argument in EAX -746 # if (EAX <= 9) return EAX + '0' -747 3d/compare-EAX-with 0x9/imm32/9 -748 7f/jump-if-greater $to-hex-char:else/disp8 -749 05/add-to-EAX 0x30/imm32/0 -750 c3/return -751 $to-hex-char:else: -752 # otherwise return EAX + 'a' - 10 -753 05/add-to-EAX 0x57/imm32/a-10 -754 c3/return -755 -756 == data -757 -758 _test-slice-empty: -759 # nothing -760 _test-slice-empty-end: -761 -762 _test-slice-hex-int: -763 33/3 34/4 -764 _test-slice-hex-int-end: -765 -766 _test-slice-hex-int-letters-negative: -767 2d/- -768 _test-slice-hex-int-letters: -769 33/3 34/4 61/a -770 _test-slice-hex-int-letters-end: -771 -772 _test-slice-hex-int-single-letter: -773 61/a -774 _test-slice-hex-int-single-letter-end: -775 -776 _test-slice-char-and-digits: -777 71/q 33/3 34/4 -778 _test-slice-char-and-digits-end: -779 -780 _test-slice-digits-and-char: -781 33/3 34/4 71/q -782 _test-slice-digits-and-char-end: -783 -784 _test-slice-hex-int-with-0x-prefix-negative: -785 2d/- -786 _test-slice-hex-int-with-0x-prefix: -787 30/0 78/x 33/3 34/4 -788 _test-slice-hex-int-with-0x-prefix-end: -789 -790 _test-slice-hex-int-zero: -791 30/0 -792 _test-slice-hex-int-zero-end: -793 -794 _test-slice-hex-int-with-0-prefix: -795 30/0 33/3 -796 _test-slice-hex-int-with-0-prefix-end: -797 -798 _test-slice-hex-int-negative-with-0-prefix: -799 2d/- 30/0 33/3 -800 _test-slice-hex-int-negative-with-0-prefix-end: -801 -802 # . . vim:nowrap:textwidth=0 +381 # . negate = true +382 be/copy-to-ESI 1/imm32/true +383 $parse-hex-int:initial-0: +384 # skip past leading '0x' +385 # . if (*curr != '0') jump to loop +386 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +387 3d/compare-EAX-and 0x30/imm32/0 +388 75/jump-if-not-equal $parse-hex-int:loop/disp8 +389 # . ++curr +390 41/increment-ECX +391 $parse-hex-int:initial-0x: +392 # . if (curr >= in->end) return result +393 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX +394 73/jump-if-greater-or-equal-unsigned $parse-hex-int:end/disp8 +395 # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again +396 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +397 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +398 3d/compare-EAX-and 0x78/imm32/x +399 75/jump-if-not-equal $parse-hex-int:loop/disp8 +400 # . ++curr +401 41/increment-ECX +402 $parse-hex-int:loop: +403 # if (curr >= in->end) break +404 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX +405 73/jump-if-greater-or-equal-unsigned $parse-hex-int:negate/disp8 +406 # EAX = from-hex-char(*curr) +407 # . . copy arg to EAX +408 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +409 # . . call +410 e8/call from-hex-char/disp32 +411 # result = result * 16 + EAX +412 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 4/imm8 # shift EBX left by 4 bits +413 01/add 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # add EAX to EBX +414 # ++curr +415 41/increment-ECX +416 # loop +417 eb/jump $parse-hex-int:loop/disp8 +418 $parse-hex-int:negate: +419 81 7/subop/compare 3/mod/direct 6/rm32/ESI . . . . . 0/imm32 # compare ESI +420 74/jump-if-equal $parse-hex-int:end/disp8 +421 f7 3/subop/negate 3/mod/direct 3/rm32/EBX . . . . . . # negate EBX +422 $parse-hex-int:end: +423 89/copy 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to EAX +424 # . restore registers +425 5e/pop-to-ESI +426 5b/pop-to-EBX +427 5a/pop-to-EDX +428 59/pop-to-ECX +429 # . epilog +430 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +431 5d/pop-to-EBP +432 c3/return +433 +434 test-parse-hex-int-single-digit: +435 # . prolog +436 55/push-EBP +437 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +438 # (EAX..ECX) = "a" +439 b8/copy-to-EAX "a"/imm32 +440 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +441 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +442 05/add-to-EAX 4/imm32 +443 # var slice/ECX = {EAX, ECX} +444 51/push-ECX +445 50/push-EAX +446 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +447 # EAX = parse-hex-int(slice) +448 # . . push args +449 51/push-ECX +450 # . . call +451 e8/call parse-hex-int/disp32 +452 # . . discard args +453 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +454 # check-ints-equal(EAX, 0xa, msg) +455 # . . push args +456 68/push "F - test-parse-hex-int-single-digit"/imm32 +457 68/push 0xa/imm32 +458 50/push-EAX +459 # . . call +460 e8/call check-ints-equal/disp32 +461 # . . discard args +462 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +463 # . epilog +464 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +465 5d/pop-to-EBP +466 c3/return +467 +468 test-parse-hex-int-multi-digit: +469 # . prolog +470 55/push-EBP +471 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +472 # (EAX..ECX) = "34a" +473 b8/copy-to-EAX "34a"/imm32 +474 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +475 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +476 05/add-to-EAX 4/imm32 +477 # var slice/ECX = {EAX, ECX} +478 51/push-ECX +479 50/push-EAX +480 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +481 # EAX = parse-hex-int(slice) +482 # . . push args +483 51/push-ECX +484 # . . call +485 e8/call parse-hex-int/disp32 +486 # . . discard args +487 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +488 # check-ints-equal(EAX, 0x34a, msg) +489 # . . push args +490 68/push "F - test-parse-hex-int-multi-digit"/imm32 +491 68/push 0x34a/imm32 +492 50/push-EAX +493 # . . call +494 e8/call check-ints-equal/disp32 +495 # . . discard args +496 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +497 # . epilog +498 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +499 5d/pop-to-EBP +500 c3/return +501 +502 test-parse-hex-int-0x-prefix: +503 # . prolog +504 55/push-EBP +505 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +506 # (EAX..ECX) = "0x34" +507 b8/copy-to-EAX "0x34"/imm32 +508 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +509 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +510 05/add-to-EAX 4/imm32 +511 # var slice/ECX = {EAX, ECX} +512 51/push-ECX +513 50/push-EAX +514 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +515 # EAX = parse-hex-int(slice) +516 # . . push args +517 51/push-ECX +518 # . . call +519 e8/call parse-hex-int/disp32 +520 # . . discard args +521 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +522 # check-ints-equal(EAX, 0x34a, msg) +523 # . . push args +524 68/push "F - test-parse-hex-int-0x-prefix"/imm32 +525 68/push 0x34/imm32 +526 50/push-EAX +527 # . . call +528 e8/call check-ints-equal/disp32 +529 # . . discard args +530 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +531 # . epilog +532 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +533 5d/pop-to-EBP +534 c3/return +535 +536 test-parse-hex-int-zero: +537 # . prolog +538 55/push-EBP +539 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +540 # (EAX..ECX) = "0" +541 b8/copy-to-EAX "0"/imm32 +542 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +543 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +544 05/add-to-EAX 4/imm32 +545 # var slice/ECX = {EAX, ECX} +546 51/push-ECX +547 50/push-EAX +548 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +549 # EAX = parse-hex-int(slice) +550 # . . push args +551 51/push-ECX +552 # . . call +553 e8/call parse-hex-int/disp32 +554 # . . discard args +555 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +556 # check-ints-equal(EAX, 0x34a, msg) +557 # . . push args +558 68/push "F - test-parse-hex-int-zero"/imm32 +559 68/push 0/imm32 +560 50/push-EAX +561 # . . call +562 e8/call check-ints-equal/disp32 +563 # . . discard args +564 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +565 # . epilog +566 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +567 5d/pop-to-EBP +568 c3/return +569 +570 test-parse-hex-int-0-prefix: +571 # . prolog +572 55/push-EBP +573 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +574 # (EAX..ECX) = "03" +575 b8/copy-to-EAX "03"/imm32 +576 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +577 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +578 05/add-to-EAX 4/imm32 +579 # var slice/ECX = {EAX, ECX} +580 51/push-ECX +581 50/push-EAX +582 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +583 # EAX = parse-hex-int(slice) +584 # . . push args +585 51/push-ECX +586 # . . call +587 e8/call parse-hex-int/disp32 +588 # . . discard args +589 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +590 # check-ints-equal(EAX, 0x3, msg) +591 # . . push args +592 68/push "F - test-parse-hex-int-0-prefix"/imm32 +593 68/push 0x3/imm32 +594 50/push-EAX +595 # . . call +596 e8/call check-ints-equal/disp32 +597 # . . discard args +598 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +599 # . epilog +600 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +601 5d/pop-to-EBP +602 c3/return +603 +604 test-parse-hex-int-negative: +605 # . prolog +606 55/push-EBP +607 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +608 # (EAX..ECX) = "-03" +609 b8/copy-to-EAX "-03"/imm32 +610 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +611 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +612 05/add-to-EAX 4/imm32 +613 # var slice/ECX = {EAX, ECX} +614 51/push-ECX +615 50/push-EAX +616 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +617 # EAX = parse-hex-int(slice) +618 # . . push args +619 51/push-ECX +620 # . . call +621 e8/call parse-hex-int/disp32 +622 # . . discard args +623 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +624 # check-ints-equal(EAX, 0xfffffffd, msg) +625 # . . push args +626 68/push "F - test-parse-hex-int-negative"/imm32 +627 68/push 0xfffffffd/imm32 +628 50/push-EAX +629 # . . call +630 e8/call check-ints-equal/disp32 +631 # . . discard args +632 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +633 # . epilog +634 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +635 5d/pop-to-EBP +636 c3/return +637 +638 is-hex-digit?: # c : byte -> EAX : boolean +639 # . prolog +640 55/push-EBP +641 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +642 # . save registers +643 51/push-ECX +644 # ECX = c +645 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +646 # return false if c < '0' +647 b8/copy-to-EAX 0/imm32/false +648 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x30/imm32 # compare ECX +649 7c/jump-if-lesser $is-hex-digit?:end/disp8 +650 # return false if c > 'f' +651 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x66/imm32 # compare ECX +652 7f/jump-if-greater $is-hex-digit?:end/disp8 +653 # return true if c <= '9' +654 b8/copy-to-EAX 1/imm32/true +655 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x39/imm32 # compare ECX +656 7e/jump-if-lesser-or-equal $is-hex-digit?:end/disp8 +657 # return true if c >= 'a' +658 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x61/imm32 # compare ECX +659 7d/jump-if-greater-or-equal $is-hex-digit?:end/disp8 +660 # otherwise return false +661 b8/copy-to-EAX 0/imm32/false +662 $is-hex-digit?:end: +663 # . restore registers +664 59/pop-to-ECX +665 # . epilog +666 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +667 5d/pop-to-EBP +668 c3/return +669 +670 test-hex-below-0: +671 # EAX = is-hex-digit?(0x2f) +672 # . . push args +673 68/push 0x2f/imm32 +674 # . . call +675 e8/call is-hex-digit?/disp32 +676 # . . discard args +677 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +678 # check-ints-equal(EAX, 0, msg) +679 # . . push args +680 68/push "F - test-hex-below-0"/imm32 +681 68/push 0/imm32/false +682 50/push-EAX +683 # . . call +684 e8/call check-ints-equal/disp32 +685 # . . discard args +686 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +687 c3/return +688 +689 test-hex-0-to-9: +690 # EAX = is-hex-digit?(0x30) +691 # . . push args +692 68/push 0x30/imm32 +693 # . . call +694 e8/call is-hex-digit?/disp32 +695 # . . discard args +696 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +697 # check-ints-equal(EAX, 1, msg) +698 # . . push args +699 68/push "F - test-hex-at-0"/imm32 +700 68/push 1/imm32/true +701 50/push-EAX +702 # . . call +703 e8/call check-ints-equal/disp32 +704 # . . discard args +705 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +706 # EAX = is-hex-digit?(0x39) +707 # . . push args +708 68/push 0x39/imm32 +709 # . . call +710 e8/call is-hex-digit?/disp32 +711 # . . discard args +712 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +713 # check-ints-equal(EAX, 1, msg) +714 # . . push args +715 68/push "F - test-hex-at-9"/imm32 +716 68/push 1/imm32/true +717 50/push-EAX +718 # . . call +719 e8/call check-ints-equal/disp32 +720 # . . discard args +721 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +722 c3/return +723 +724 test-hex-above-9-to-a: +725 # EAX = is-hex-digit?(0x3a) +726 # . . push args +727 68/push 0x3a/imm32 +728 # . . call +729 e8/call is-hex-digit?/disp32 +730 # . . discard args +731 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +732 # check-ints-equal(EAX, 0, msg) +733 # . . push args +734 68/push "F - test-hex-above-9-to-a"/imm32 +735 68/push 0/imm32/false +736 50/push-EAX +737 # . . call +738 e8/call check-ints-equal/disp32 +739 # . . discard args +740 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +741 c3/return +742 +743 test-hex-a-to-f: +744 # EAX = is-hex-digit?(0x61) +745 # . . push args +746 68/push 0x61/imm32 +747 # . . call +748 e8/call is-hex-digit?/disp32 +749 # . . discard args +750 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +751 # check-ints-equal(EAX, 1, msg) +752 # . . push args +753 68/push "F - test-hex-at-a"/imm32 +754 68/push 1/imm32/true +755 50/push-EAX +756 # . . call +757 e8/call check-ints-equal/disp32 +758 # . . discard args +759 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +760 # EAX = is-hex-digit?(0x66) +761 # . . push args +762 68/push 0x66/imm32 +763 # . . call +764 e8/call is-hex-digit?/disp32 +765 # . . discard args +766 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +767 # check-ints-equal(EAX, 1, msg) +768 # . . push args +769 68/push "F - test-hex-at-f"/imm32 +770 68/push 1/imm32/true +771 50/push-EAX +772 # . . call +773 e8/call check-ints-equal/disp32 +774 # . . discard args +775 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +776 c3/return +777 +778 test-hex-above-f: +779 # EAX = is-hex-digit?(0x67) +780 # . . push args +781 68/push 0x67/imm32 +782 # . . call +783 e8/call is-hex-digit?/disp32 +784 # . . discard args +785 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +786 # check-ints-equal(EAX, 0, msg) +787 # . . push args +788 68/push "F - test-hex-above-f"/imm32 +789 68/push 0/imm32/false +790 50/push-EAX +791 # . . call +792 e8/call check-ints-equal/disp32 +793 # . . discard args +794 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +795 c3/return +796 +797 from-hex-char: # in/EAX : byte -> out/EAX : nibble +798 # no error checking; accepts argument in EAX +799 # if (EAX <= '9') return EAX - '0' +800 3d/compare-EAX-with 0x39/imm32/9 +801 7f/jump-if-greater $from-hex-char:else/disp8 +802 2d/subtract-from-EAX 0x30/imm32/0 +803 c3/return +804 $from-hex-char:else: +805 # otherwise return EAX - 'a' + 10 +806 2d/subtract-from-EAX 0x57/imm32/a-10 +807 c3/return +808 +809 to-hex-char: # in/EAX : nibble -> out/EAX : byte +810 # no error checking; accepts argument in EAX +811 # if (EAX <= 9) return EAX + '0' +812 3d/compare-EAX-with 0x9/imm32/9 +813 7f/jump-if-greater $to-hex-char:else/disp8 +814 05/add-to-EAX 0x30/imm32/0 +815 c3/return +816 $to-hex-char:else: +817 # otherwise return EAX + 'a' - 10 +818 05/add-to-EAX 0x57/imm32/a-10 +819 c3/return +820 +821 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/066write-buffered.subx.html b/html/subx/066write-buffered.subx.html index e91e2060..ffb79b3c 100644 --- a/html/subx/066write-buffered.subx.html +++ b/html/subx/066write-buffered.subx.html @@ -3,8 +3,8 @@ Mu - subx/066write-buffered.subx - - + + @@ -14,16 +14,15 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } .subxS1Comment { color: #0000af; } -.CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.LineNr { } .subxTest { color: #5f8700; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } .subxS2Comment { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } --> @@ -40,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -66,217 +65,209 @@ if ('onhashchange' in window) { 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 a single test, while debugging - 9 #? e8/call test-write-buffered/disp32 - 10 #? e8/call test-write-buffered-with-intermediate-flush/disp32 - 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 #? b8/copy-to-EAX 1/imm32/exit - 14 #? cd/syscall 0x80/imm8 - 15 - 16 write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void> - 17 # pseudocode: - 18 # in = msg->data - 19 # inend = &msg->data[msg->length] - 20 # while (in < inend) - 21 # if f->write >= f->length - 22 # flush(f) - 23 # clear-stream(f) - 24 # c = *in - 25 # f->data[f->write] = c - 26 # ++f->write - 27 # ++in + 8 write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void> + 9 # pseudocode: + 10 # in = msg->data + 11 # inend = &msg->data[msg->length] + 12 # while (in < inend) + 13 # if f->write >= f->length + 14 # flush(f) + 15 # clear-stream(f) + 16 # c = *in + 17 # f->data[f->write] = c + 18 # ++f->write + 19 # ++in + 20 # + 21 # registers: + 22 # in: ESI + 23 # inend: ECX + 24 # f: EDI + 25 # f->length: EDX + 26 # f->write: EBX (cached; need to keep in sync) + 27 # c: EAX 28 # - 29 # registers: - 30 # in: ESI - 31 # inend: ECX - 32 # f: EDI - 33 # f->length: EDX - 34 # f->write: EBX (cached; need to keep in sync) - 35 # c: EAX - 36 # - 37 # . prolog - 38 55/push-EBP - 39 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 40 # . save registers - 41 50/push-EAX - 42 51/push-ECX - 43 52/push-EDX - 44 53/push-EBX - 45 56/push-ESI - 46 57/push-EDI - 47 # EAX = msg - 48 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 49 # in/ESI = msg->data - 50 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 6/r32/ESI 4/disp8 . # copy EAX+4 to ESI - 51 # inend/ECX = &msg->data[msg->length] - 52 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 53 8d/copy-address 0/mod/indirect 4/rm32/sib 6/base/ESI 1/index/ECX . 1/r32/ECX . . # copy ESI+ECX to ECX - 54 # EDI = f - 55 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - 56 # EDX = f->length - 57 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 0xc/disp8 . # copy *(EDI+12) to EDX - 58 # EBX = f->write - 59 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy *(EDI+4) to EBX - 60 $write-buffered:loop: - 61 # if (in >= inend) break - 62 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX - 63 7d/jump-if-greater-or-equal $write-buffered:loop-end/disp8 - 64 # if (f->write >= f->length) flush and clear f's stream - 65 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX - 66 7c/jump-if-lesser $write-buffered:to-stream/disp8 - 67 # . persist f->write - 68 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) - 69 # . flush(f) - 70 # . . push args - 71 57/push-EDI + 29 # . prolog + 30 55/push-EBP + 31 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 32 # . save registers + 33 50/push-EAX + 34 51/push-ECX + 35 52/push-EDX + 36 53/push-EBX + 37 56/push-ESI + 38 57/push-EDI + 39 # EAX = msg + 40 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 41 # in/ESI = msg->data + 42 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 6/r32/ESI 4/disp8 . # copy EAX+4 to ESI + 43 # inend/ECX = &msg->data[msg->length] + 44 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 45 8d/copy-address 0/mod/indirect 4/rm32/sib 6/base/ESI 1/index/ECX . 1/r32/ECX . . # copy ESI+ECX to ECX + 46 # EDI = f + 47 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + 48 # EDX = f->length + 49 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 0xc/disp8 . # copy *(EDI+12) to EDX + 50 # EBX = f->write + 51 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy *(EDI+4) to EBX + 52 $write-buffered:loop: + 53 # if (in >= inend) break + 54 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX + 55 73/jump-if-greater-or-equal-unsigned $write-buffered:loop-end/disp8 + 56 # if (f->write >= f->length) flush and clear f's stream + 57 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX + 58 7c/jump-if-lesser $write-buffered:to-stream/disp8 + 59 # . persist f->write + 60 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) + 61 # . flush(f) + 62 # . . push args + 63 57/push-EDI + 64 # . . call + 65 e8/call flush/disp32 + 66 # . . discard args + 67 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 68 # . clear-stream(stream = f+4) + 69 # . . push args + 70 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+4 to EAX + 71 50/push-EAX 72 # . . call - 73 e8/call flush/disp32 + 73 e8/call clear-stream/disp32 74 # . . discard args 75 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 76 # . clear-stream(stream = f+4) - 77 # . . push args - 78 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+4 to EAX - 79 50/push-EAX - 80 # . . call - 81 e8/call clear-stream/disp32 - 82 # . . discard args - 83 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 84 # . f->write must now be 0; update its cache at EBX - 85 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 86 $write-buffered:to-stream: - 87 # write to stream - 88 # f->data[f->write] = *in - 89 # . AL = *in - 90 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 91 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - 92 # . f->data[f->write] = AL - 93 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+EBX+16) - 94 # ++f->write - 95 43/increment-EBX - 96 # ++in - 97 46/increment-ESI - 98 eb/jump $write-buffered:loop/disp8 - 99 $write-buffered:loop-end: -100 # persist necessary variables from registers -101 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) -102 $write-buffered:end: -103 # . restore registers -104 5f/pop-to-EDI -105 5e/pop-to-ESI -106 5b/pop-to-EBX -107 5a/pop-to-EDX -108 59/pop-to-ECX -109 58/pop-to-EAX -110 # . epilog -111 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -112 5d/pop-to-EBP -113 c3/return -114 -115 test-write-buffered: -116 # - check that write-buffered writes to the file -117 # setup -118 # . clear-stream(_test-stream) -119 # . . push args -120 68/push _test-stream/imm32 -121 # . . call -122 e8/call clear-stream/disp32 -123 # . . discard args -124 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -125 # . clear-stream(_test-buffered-file+4) -126 # . . push args -127 b8/copy-to-EAX _test-buffered-file/imm32 -128 05/add-to-EAX 4/imm32 -129 50/push-EAX + 76 # . f->write must now be 0; update its cache at EBX + 77 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX + 78 $write-buffered:to-stream: + 79 # write to stream + 80 # f->data[f->write] = *in + 81 # . AL = *in + 82 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 83 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL + 84 # . f->data[f->write] = AL + 85 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+EBX+16) + 86 # ++f->write + 87 43/increment-EBX + 88 # ++in + 89 46/increment-ESI + 90 eb/jump $write-buffered:loop/disp8 + 91 $write-buffered:loop-end: + 92 # persist necessary variables from registers + 93 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) + 94 $write-buffered:end: + 95 # . restore registers + 96 5f/pop-to-EDI + 97 5e/pop-to-ESI + 98 5b/pop-to-EBX + 99 5a/pop-to-EDX +100 59/pop-to-ECX +101 58/pop-to-EAX +102 # . epilog +103 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +104 5d/pop-to-EBP +105 c3/return +106 +107 test-write-buffered: +108 # - check that write-buffered writes to the file +109 # setup +110 # . clear-stream(_test-stream) +111 # . . push args +112 68/push _test-stream/imm32 +113 # . . call +114 e8/call clear-stream/disp32 +115 # . . discard args +116 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +117 # . clear-stream(_test-buffered-file+4) +118 # . . push args +119 b8/copy-to-EAX _test-buffered-file/imm32 +120 05/add-to-EAX 4/imm32 +121 50/push-EAX +122 # . . call +123 e8/call clear-stream/disp32 +124 # . . discard args +125 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +126 # write-buffered(_test-buffered-file, "Abc") +127 # . . push args +128 68/push "Abc"/imm32 +129 68/push _test-buffered-file/imm32 130 # . . call -131 e8/call clear-stream/disp32 +131 e8/call write-buffered/disp32 132 # . . discard args -133 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -134 # write-buffered(_test-buffered-file, "Abc") +133 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +134 # flush(_test-buffered-file) 135 # . . push args -136 68/push "Abc"/imm32 -137 68/push _test-buffered-file/imm32 -138 # . . call -139 e8/call write-buffered/disp32 -140 # . . discard args -141 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -142 # flush(_test-buffered-file) -143 # . . push args -144 68/push _test-buffered-file/imm32 -145 # . . call -146 e8/call flush/disp32 -147 # . . discard args -148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -149 # check-stream-equal(_test-stream, "Abc", msg) -150 # . . push args -151 68/push "F - test-write-buffered-single"/imm32 -152 68/push "Abc"/imm32 -153 68/push _test-stream/imm32 -154 # . . call -155 e8/call check-stream-equal/disp32 -156 # . . discard args -157 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -158 # . end -159 c3/return -160 -161 test-write-buffered-with-intermediate-flush: -162 # - check that write-buffered flushes in the middle if its buffer fills up -163 # setup -164 # . clear-stream(_test-stream) -165 # . . push args -166 68/push _test-stream/imm32 -167 # . . call -168 e8/call clear-stream/disp32 -169 # . . discard args -170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -171 # . clear-stream(_test-buffered-file+4) -172 # . . push args -173 b8/copy-to-EAX _test-buffered-file/imm32 -174 05/add-to-EAX 4/imm32 -175 50/push-EAX -176 # . . call -177 e8/call clear-stream/disp32 -178 # . . discard args -179 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -180 # _test-stream can hold 8 bytes, but _test-buffered-file can hold only 6. -181 # Try to write 7 bytes. -182 # . write-buffered(_test-buffered-file, "Abcdefg") -183 # . . push args -184 68/push "Abcdefg"/imm32 -185 68/push _test-buffered-file/imm32 -186 # . . call -187 e8/call write-buffered/disp32 -188 # . . discard args -189 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -190 # don't flush -191 # 6 bytes should still have gotten to _test-stream -192 # . check-ints-equal(*_test-stream->write, 6, msg) -193 # . . push args -194 68/push "F - test-write-buffered-with-intermediate-flush: flushed data"/imm32 -195 68/push 6/imm32 -196 # . . push *_test-stream->write -197 b8/copy-to-EAX _test-stream/imm32 -198 ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX -199 # . . call -200 e8/call check-ints-equal/disp32 -201 # . . discard args -202 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -203 # and 1 byte should still be in _test-buffered-file -204 # . check-ints-equal(*_test-buffered-file->write, 1, msg) -205 # . . push args -206 68/push "F - test-write-buffered-with-intermediate-flush: unflushed bytes"/imm32 -207 68/push 1/imm32 -208 # . . push *_test-buffered-file->write -209 b8/copy-to-EAX _test-buffered-file/imm32 -210 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -211 # . . call -212 e8/call check-ints-equal/disp32 -213 # . . discard args -214 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -215 # . end -216 c3/return -217 -218 # . . vim:nowrap:textwidth=0 +136 68/push _test-buffered-file/imm32 +137 # . . call +138 e8/call flush/disp32 +139 # . . discard args +140 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +141 # check-stream-equal(_test-stream, "Abc", msg) +142 # . . push args +143 68/push "F - test-write-buffered-single"/imm32 +144 68/push "Abc"/imm32 +145 68/push _test-stream/imm32 +146 # . . call +147 e8/call check-stream-equal/disp32 +148 # . . discard args +149 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +150 # . end +151 c3/return +152 +153 test-write-buffered-with-intermediate-flush: +154 # - check that write-buffered flushes in the middle if its buffer fills up +155 # setup +156 # . clear-stream(_test-stream) +157 # . . push args +158 68/push _test-stream/imm32 +159 # . . call +160 e8/call clear-stream/disp32 +161 # . . discard args +162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +163 # . clear-stream(_test-buffered-file+4) +164 # . . push args +165 b8/copy-to-EAX _test-buffered-file/imm32 +166 05/add-to-EAX 4/imm32 +167 50/push-EAX +168 # . . call +169 e8/call clear-stream/disp32 +170 # . . discard args +171 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +172 # _test-stream can hold 8 bytes, but _test-buffered-file can hold only 6. +173 # Try to write 7 bytes. +174 # . write-buffered(_test-buffered-file, "Abcdefg") +175 # . . push args +176 68/push "Abcdefg"/imm32 +177 68/push _test-buffered-file/imm32 +178 # . . call +179 e8/call write-buffered/disp32 +180 # . . discard args +181 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +182 # don't flush +183 # 6 bytes should still have gotten to _test-stream +184 # . check-ints-equal(*_test-stream->write, 6, msg) +185 # . . push args +186 68/push "F - test-write-buffered-with-intermediate-flush: flushed data"/imm32 +187 68/push 6/imm32 +188 # . . push *_test-stream->write +189 b8/copy-to-EAX _test-stream/imm32 +190 ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX +191 # . . call +192 e8/call check-ints-equal/disp32 +193 # . . discard args +194 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +195 # and 1 byte should still be in _test-buffered-file +196 # . check-ints-equal(*_test-buffered-file->write, 1, msg) +197 # . . push args +198 68/push "F - test-write-buffered-with-intermediate-flush: unflushed bytes"/imm32 +199 68/push 1/imm32 +200 # . . push *_test-buffered-file->write +201 b8/copy-to-EAX _test-buffered-file/imm32 +202 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +203 # . . call +204 e8/call check-ints-equal/disp32 +205 # . . discard args +206 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +207 # . end +208 c3/return +209 +210 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/067print-int.subx.html b/html/subx/067print-int.subx.html index 3f9ddcf5..0654d7d7 100644 --- a/html/subx/067print-int.subx.html +++ b/html/subx/067print-int.subx.html @@ -3,8 +3,8 @@ Mu - subx/067print-int.subx - - + + @@ -14,17 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.Folded { color: #080808; background-color: #949494; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.subxTest { color: #5f8700; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Folded { color: #080808; background-color: #949494; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -41,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -67,366 +66,376 @@ if ('onhashchange' in window) { 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 a single test, while debugging - 9 #? e8/call test-print-int32/disp32 - 10 #? # syscall(exit, Num-test-failures) - 11 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 12 #? b8/copy-to-EAX 1/imm32/exit - 13 #? cd/syscall 0x80/imm8 - 14 - 15 append-byte-hex: # f : (address stream), n : int -> <void> - 16 # . prolog - 17 55/push-EBP - 18 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 19 # . save registers - 20 50/push-EAX - 21 # AL = convert upper nibble to hex - 22 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 23 c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/EAX . . . . . 4/imm8 # shift EAX right by 4 bits, while padding zeroes - 24 25/and-EAX 0xf/imm32 - 25 # . AL = to-hex-char(AL) - 26 e8/call to-hex-char/disp32 - 27 # append-byte(f, AL) - 28 # . . push args - 29 50/push-EAX - 30 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 31 # . . call - 32 e8/call append-byte/disp32 - 33 # . . discard args - 34 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 35 # AL = convert lower nibble to hex - 36 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 37 25/and-EAX 0xf/imm32 - 38 # . AL = to-hex-char(AL) - 39 e8/call to-hex-char/disp32 - 40 # append-byte(f, AL) - 41 # . . push args - 42 50/push-EAX - 43 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 44 # . . call - 45 e8/call append-byte/disp32 - 46 # . . discard args - 47 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 48 $append-byte-hex:end: - 49 # . restore registers - 50 58/pop-to-EAX - 51 # . epilog - 52 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 53 5d/pop-to-EBP - 54 c3/return - 55 - 56 test-append-byte-hex: - 57 # - check that append-byte-hex adds the hex textual representation - 58 # setup - 59 # . clear-stream(_test-stream) + 8 append-byte-hex: # f : (address stream), n : int -> <void> + 9 # . prolog + 10 55/push-EBP + 11 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 12 # . save registers + 13 50/push-EAX + 14 # AL = convert upper nibble to hex + 15 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 16 c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/EAX . . . . . 4/imm8 # shift EAX right by 4 bits, while padding zeroes + 17 25/and-EAX 0xf/imm32 + 18 # . AL = to-hex-char(AL) + 19 e8/call to-hex-char/disp32 + 20 # append-byte(f, AL) + 21 # . . push args + 22 50/push-EAX + 23 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 24 # . . call + 25 e8/call append-byte/disp32 + 26 # . . discard args + 27 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 28 # AL = convert lower nibble to hex + 29 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 30 25/and-EAX 0xf/imm32 + 31 # . AL = to-hex-char(AL) + 32 e8/call to-hex-char/disp32 + 33 # append-byte(f, AL) + 34 # . . push args + 35 50/push-EAX + 36 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 37 # . . call + 38 e8/call append-byte/disp32 + 39 # . . discard args + 40 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 41 $append-byte-hex:end: + 42 # . restore registers + 43 58/pop-to-EAX + 44 # . epilog + 45 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 46 5d/pop-to-EBP + 47 c3/return + 48 + 49 test-append-byte-hex: + 50 # - check that append-byte-hex adds the hex textual representation + 51 # setup + 52 # . clear-stream(_test-stream) + 53 # . . push args + 54 68/push _test-stream/imm32 + 55 # . . call + 56 e8/call clear-stream/disp32 + 57 # . . discard args + 58 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 59 # append-byte-hex(_test-stream, 0xa) # exercises digit, non-digit as well as leading zero 60 # . . push args - 61 68/push _test-stream/imm32 - 62 # . . call - 63 e8/call clear-stream/disp32 - 64 # . . discard args - 65 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 66 # append-byte-hex(_test-stream, 0xa) # exercises digit, non-digit as well as leading zero - 67 # . . push args - 68 68/push 0xa/imm32 - 69 68/push _test-stream/imm32 - 70 # . . call - 71 e8/call append-byte-hex/disp32 - 72 # . . discard args - 73 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 74 # check-stream-equal(_test-stream, "0a", msg) - 75 # . . push args - 76 68/push "F - test-append-byte-hex"/imm32 - 77 68/push "0a"/imm32 - 78 68/push _test-stream/imm32 - 79 # . . call - 80 e8/call check-stream-equal/disp32 - 81 # . . discard args - 82 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 83 # . end - 84 c3/return - 85 - 86 # print the hex representation for the lowest byte of a number - 87 print-byte-buffered: # f : (address buffered-file), n : int -> <void> - 88 # . prolog - 89 55/push-EBP - 90 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 91 # . save registers - 92 50/push-EAX - 93 # AL = convert upper nibble to hex - 94 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 95 c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/EAX . . . . . 4/imm8 # shift EAX right by 4 bits, while padding zeroes - 96 25/and-EAX 0xf/imm32 - 97 # . AL = to-hex-char(AL) - 98 e8/call to-hex-char/disp32 - 99 # write-byte-buffered(f, AL) -100 # . . push args -101 50/push-EAX -102 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -103 # . . call -104 e8/call write-byte-buffered/disp32 -105 # . . discard args -106 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -107 # AL = convert lower nibble to hex -108 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX -109 25/and-EAX 0xf/imm32 -110 # . AL = to-hex-char(AL) -111 e8/call to-hex-char/disp32 -112 # write-byte-buffered(f, AL) -113 # . . push args -114 50/push-EAX -115 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -116 # . . call -117 e8/call write-byte-buffered/disp32 -118 # . . discard args -119 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -120 $print-byte-buffered:end: -121 # . restore registers -122 58/pop-to-EAX -123 # . epilog -124 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -125 5d/pop-to-EBP -126 c3/return -127 -128 test-print-byte-buffered: -129 # - check that print-byte-buffered prints the hex textual representation -130 # setup -131 # . clear-stream(_test-stream) + 61 68/push 0xa/imm32 + 62 68/push _test-stream/imm32 + 63 # . . call + 64 e8/call append-byte-hex/disp32 + 65 # . . discard args + 66 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 67 # check-stream-equal(_test-stream, "0a", msg) + 68 # . . push args + 69 68/push "F - test-append-byte-hex"/imm32 + 70 68/push "0a"/imm32 + 71 68/push _test-stream/imm32 + 72 # . . call + 73 e8/call check-stream-equal/disp32 + 74 # . . discard args + 75 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 76 # . end + 77 c3/return + 78 + 79 # print the hex representation for the lowest byte of a number + 80 print-byte-buffered: # f : (address buffered-file), n : int -> <void> + 81 # . prolog + 82 55/push-EBP + 83 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 84 # . save registers + 85 50/push-EAX + 86 # AL = convert upper nibble to hex + 87 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 88 c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/EAX . . . . . 4/imm8 # shift EAX right by 4 bits, while padding zeroes + 89 25/and-EAX 0xf/imm32 + 90 # . AL = to-hex-char(AL) + 91 e8/call to-hex-char/disp32 + 92 # write-byte-buffered(f, AL) + 93 # . . push args + 94 50/push-EAX + 95 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 96 # . . call + 97 e8/call write-byte-buffered/disp32 + 98 # . . discard args + 99 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +100 # AL = convert lower nibble to hex +101 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX +102 25/and-EAX 0xf/imm32 +103 # . AL = to-hex-char(AL) +104 e8/call to-hex-char/disp32 +105 # write-byte-buffered(f, AL) +106 # . . push args +107 50/push-EAX +108 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +109 # . . call +110 e8/call write-byte-buffered/disp32 +111 # . . discard args +112 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +113 $print-byte-buffered:end: +114 # . restore registers +115 58/pop-to-EAX +116 # . epilog +117 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +118 5d/pop-to-EBP +119 c3/return +120 +121 test-print-byte-buffered: +122 # - check that print-byte-buffered prints the hex textual representation +123 # setup +124 # . clear-stream(_test-stream) +125 # . . push args +126 68/push _test-stream/imm32 +127 # . . call +128 e8/call clear-stream/disp32 +129 # . . discard args +130 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +131 # . clear-stream(_test-buffered-file+4) 132 # . . push args -133 68/push _test-stream/imm32 -134 # . . call -135 e8/call clear-stream/disp32 -136 # . . discard args -137 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -138 # . clear-stream(_test-buffered-file+4) -139 # . . push args -140 b8/copy-to-EAX _test-buffered-file/imm32 -141 05/add-to-EAX 4/imm32 -142 50/push-EAX -143 # . . call -144 e8/call clear-stream/disp32 -145 # . . discard args -146 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -147 # print-byte-buffered(_test-buffered-file, 0xa) # exercises digit, non-digit as well as leading zero -148 # . . push args -149 68/push 0xa/imm32 -150 68/push _test-buffered-file/imm32 +133 b8/copy-to-EAX _test-buffered-file/imm32 +134 05/add-to-EAX 4/imm32 +135 50/push-EAX +136 # . . call +137 e8/call clear-stream/disp32 +138 # . . discard args +139 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +140 # print-byte-buffered(_test-buffered-file, 0xa) # exercises digit, non-digit as well as leading zero +141 # . . push args +142 68/push 0xa/imm32 +143 68/push _test-buffered-file/imm32 +144 # . . call +145 e8/call print-byte-buffered/disp32 +146 # . . discard args +147 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +148 # flush(_test-buffered-file) +149 # . . push args +150 68/push _test-buffered-file/imm32 151 # . . call -152 e8/call print-byte-buffered/disp32 +152 e8/call flush/disp32 153 # . . discard args -154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -155 # flush(_test-buffered-file) +154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +155 # check-stream-equal(_test-stream, "0a", msg) 156 # . . push args -157 68/push _test-buffered-file/imm32 -158 # . . call -159 e8/call flush/disp32 -160 # . . discard args -161 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -162 # check-stream-equal(_test-stream, "0a", msg) -163 # . . push args -164 68/push "F - test-print-byte-buffered"/imm32 -165 68/push "0a"/imm32 -166 68/push _test-stream/imm32 -167 # . . call -168 e8/call check-stream-equal/disp32 -169 # . . discard args -170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -171 # . end -172 c3/return -173 -174 print-int32: # f : (address stream), n : int -> <void> -175 # pseudocode: -176 # write(f, "0x") -177 # ECX = 28 -178 # while true -179 # if (ECX < 0) break -180 # EAX = n >> ECX -181 # EAX = EAX & 0xf -182 # append-byte(f, AL) -183 # ECX -= 4 -184 # -185 # . prolog -186 55/push-EBP -187 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -188 # . save registers -189 50/push-EAX -190 51/push-ECX -191 # ECX = 28 -192 b9/copy-to-ECX 0x1c/imm32 -193 $print-int32:print-hex-prefix: -194 # write(f, "0x") -195 # . . push args -196 68/push "0x"/imm32 -197 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -198 # . . call -199 e8/call write/disp32 -200 # . . discard args -201 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -202 $print-int32:loop: -203 # if (ECX < 0) break -204 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX -205 7c/jump-if-lesser $print-int32:end/disp8 -206 # EAX = n >> ECX -207 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX -208 d3/>>ECX 5/subop/pad-zeroes 3/mod/direct 0/rm32/EAX . . . . . . # shift EAX right by ECX bits, padding zeroes -209 # EAX = to-hex-char(AL) -210 25/and-EAX 0xf/imm32 -211 e8/call to-hex-char/disp32 -212 # append-byte(f, AL) -213 # . . push args -214 50/push-EAX -215 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -216 # . . call -217 e8/call append-byte/disp32 -218 # . . discard args -219 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -220 # ECX -= 4 -221 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # subtract from ECX -222 eb/jump $print-int32:loop/disp8 -223 $print-int32:end: -224 # . restore registers -225 59/pop-to-ECX -226 58/pop-to-EAX -227 # . epilog -228 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -229 5d/pop-to-EBP -230 c3/return -231 -232 test-print-int32: -233 # - check that print-int32 prints the hex textual representation -234 # setup -235 # . clear-stream(_test-stream) +157 68/push "F - test-print-byte-buffered"/imm32 +158 68/push "0a"/imm32 +159 68/push _test-stream/imm32 +160 # . . call +161 e8/call check-stream-equal/disp32 +162 # . . discard args +163 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +164 # . end +165 c3/return +166 +167 print-int32: # f : (address stream), n : int -> <void> +168 # pseudocode: +169 # write(f, "0x") +170 # ECX = 28 +171 # while true +172 # if (ECX < 0) break +173 # EAX = n >> ECX +174 # EAX = EAX & 0xf +175 # append-byte(f, AL) +176 # ECX -= 4 +177 # +178 # . prolog +179 55/push-EBP +180 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +181 # . save registers +182 50/push-EAX +183 51/push-ECX +184 # ECX = 28 +185 b9/copy-to-ECX 0x1c/imm32 +186 $print-int32:print-hex-prefix: +187 # write(f, "0x") +188 # . . push args +189 68/push "0x"/imm32 +190 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +191 # . . call +192 e8/call write/disp32 +193 # . . discard args +194 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +195 $print-int32:loop: +196 # if (ECX < 0) break +197 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX +198 7c/jump-if-lesser $print-int32:end/disp8 +199 # EAX = n >> ECX +200 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX +201 d3/>>ECX 5/subop/pad-zeroes 3/mod/direct 0/rm32/EAX . . . . . . # shift EAX right by ECX bits, padding zeroes +202 # EAX = to-hex-char(AL) +203 25/and-EAX 0xf/imm32 +204 e8/call to-hex-char/disp32 +205 # append-byte(f, AL) +206 # . . push args +207 50/push-EAX +208 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +209 # . . call +210 e8/call append-byte/disp32 +211 # . . discard args +212 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +213 # ECX -= 4 +214 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # subtract from ECX +215 eb/jump $print-int32:loop/disp8 +216 $print-int32:end: +217 # . restore registers +218 59/pop-to-ECX +219 58/pop-to-EAX +220 # . epilog +221 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +222 5d/pop-to-EBP +223 c3/return +224 +225 test-print-int32: +226 # - check that print-int32 prints the hex textual representation +227 # setup +228 # . clear-stream(_test-stream) +229 # . . push args +230 68/push _test-stream/imm32 +231 # . . call +232 e8/call clear-stream/disp32 +233 # . . discard args +234 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +235 # print-int32(_test-stream, 0x8899aa) 236 # . . push args -237 68/push _test-stream/imm32 -238 # . . call -239 e8/call clear-stream/disp32 -240 # . . discard args -241 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -242 # print-int32(_test-stream, 0x8899aa) -243 # . . push args -244 68/push 0x8899aa/imm32 -245 68/push _test-stream/imm32 -246 # . . call -247 e8/call print-int32/disp32 -248 # . . discard args -249 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -250 # check-stream-equal(_test-stream, "0x008899aa", msg) -251 # . . push args -252 68/push "F - test-print-int32"/imm32 -253 68/push "0x008899aa"/imm32 -254 68/push _test-stream/imm32 -255 # . . call -256 e8/call check-stream-equal/disp32 -257 # . . discard args -258 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -259 # . end -260 c3/return -261 -262 print-int32-buffered: # f : (address buffered-file), n : int -> <void> -263 # pseudocode: -264 # write-buffered(f, "0x") -265 # ECX = 28 -266 # while true -267 # if (ECX < 0) break -268 # EAX = n >> ECX -269 # EAX = EAX & 0xf -270 # write-byte-buffered(f, AL) -271 # ECX -= 4 -272 # -273 # . prolog -274 55/push-EBP -275 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -276 # . save registers -277 50/push-EAX -278 51/push-ECX -279 # ECX = 28 -280 b9/copy-to-ECX 0x1c/imm32 -281 $print-int32-buffered:print-hex-prefix: -282 # write-buffered(f, "0x") -283 # . . push args -284 68/push "0x"/imm32 -285 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -286 # . . call -287 e8/call write-buffered/disp32 -288 # . . discard args -289 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -290 $print-int32-buffered:loop: -291 # if (ECX < 0) break -292 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX -293 7c/jump-if-lesser $print-int32-buffered:end/disp8 -294 # EAX = n >> ECX -295 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX -296 d3/>>ECX 5/subop/pad-zeroes 3/mod/direct 0/rm32/EAX . . . . . . # shift EAX right by ECX bits, padding zeroes -297 # EAX = to-hex-char(AL) -298 25/and-EAX 0xf/imm32 -299 e8/call to-hex-char/disp32 -300 # write-byte-buffered(f, AL) -301 # . . push args -302 50/push-EAX -303 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -304 # . . call -305 e8/call write-byte-buffered/disp32 -306 # . . discard args -307 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -308 # ECX -= 4 -309 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # subtract from ECX -310 eb/jump $print-int32-buffered:loop/disp8 -311 $print-int32-buffered:end: -312 # . restore registers -313 59/pop-to-ECX -314 58/pop-to-EAX -315 # . epilog -316 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -317 5d/pop-to-EBP -318 c3/return -319 -320 test-print-int32-buffered: -321 # - check that print-int32-buffered prints the hex textual representation -322 # setup -323 # . clear-stream(_test-stream) -324 # . . push args -325 68/push _test-stream/imm32 -326 # . . call -327 e8/call clear-stream/disp32 -328 # . . discard args -329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -330 # . clear-stream(_test-buffered-file+4) -331 # . . push args -332 b8/copy-to-EAX _test-buffered-file/imm32 -333 05/add-to-EAX 4/imm32 -334 50/push-EAX -335 # . . call -336 e8/call clear-stream/disp32 -337 # . . discard args -338 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -339 # print-int32-buffered(_test-buffered-file, 0x8899aa) -340 # . . push args -341 68/push 0x8899aa/imm32 -342 68/push _test-buffered-file/imm32 -343 # . . call -344 e8/call print-int32-buffered/disp32 -345 # . . discard args -346 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -347 # flush(_test-buffered-file) -348 # . . push args -349 68/push _test-buffered-file/imm32 -350 # . . call -351 e8/call flush/disp32 -352 # . . discard args -353 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -354 +-- 18 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- -372 # check-stream-equal(_test-stream, "0x008899aa", msg) -373 # . . push args -374 68/push "F - test-print-int32-buffered"/imm32 -375 68/push "0x008899aa"/imm32 -376 68/push _test-stream/imm32 -377 # . . call -378 e8/call check-stream-equal/disp32 -379 # . . discard args -380 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -381 # . end -382 c3/return -383 -384 # . . vim:nowrap:textwidth=0 +237 68/push 0x8899aa/imm32 +238 68/push _test-stream/imm32 +239 # . . call +240 e8/call print-int32/disp32 +241 # . . discard args +242 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +243 # check-stream-equal(_test-stream, "0x008899aa", msg) +244 # . . push args +245 68/push "F - test-print-int32"/imm32 +246 68/push "0x008899aa"/imm32 +247 68/push _test-stream/imm32 +248 # . . call +249 e8/call check-stream-equal/disp32 +250 # . . discard args +251 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +252 # . end +253 c3/return +254 +255 # TODO: append to string +256 check-ints-equal2: # (a : int, b : int, msg : (address array byte)) +257 # . prolog +258 55/push-EBP +259 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +260 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +261 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +262 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +263 e8/call check-ints-equal/disp32 +264 # . . discard args +265 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +266 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +267 # . epilog +268 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +269 5d/pop-to-EBP +270 c3/return +271 +272 print-int32-buffered: # f : (address buffered-file), n : int -> <void> +273 # pseudocode: +274 # write-buffered(f, "0x") +275 # ECX = 28 +276 # while true +277 # if (ECX < 0) break +278 # EAX = n >> ECX +279 # EAX = EAX & 0xf +280 # write-byte-buffered(f, AL) +281 # ECX -= 4 +282 # +283 # . prolog +284 55/push-EBP +285 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +286 # . save registers +287 50/push-EAX +288 51/push-ECX +289 # ECX = 28 +290 b9/copy-to-ECX 0x1c/imm32 +291 $print-int32-buffered:print-hex-prefix: +292 # write-buffered(f, "0x") +293 # . . push args +294 68/push "0x"/imm32 +295 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +296 # . . call +297 e8/call write-buffered/disp32 +298 # . . discard args +299 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +300 $print-int32-buffered:loop: +301 # if (ECX < 0) break +302 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX +303 7c/jump-if-lesser $print-int32-buffered:end/disp8 +304 # EAX = n >> ECX +305 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX +306 d3/>>ECX 5/subop/pad-zeroes 3/mod/direct 0/rm32/EAX . . . . . . # shift EAX right by ECX bits, padding zeroes +307 # EAX = to-hex-char(AL) +308 25/and-EAX 0xf/imm32 +309 e8/call to-hex-char/disp32 +310 # write-byte-buffered(f, AL) +311 # . . push args +312 50/push-EAX +313 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +314 # . . call +315 e8/call write-byte-buffered/disp32 +316 # . . discard args +317 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +318 # ECX -= 4 +319 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # subtract from ECX +320 eb/jump $print-int32-buffered:loop/disp8 +321 $print-int32-buffered:end: +322 # . restore registers +323 59/pop-to-ECX +324 58/pop-to-EAX +325 # . epilog +326 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +327 5d/pop-to-EBP +328 c3/return +329 +330 test-print-int32-buffered: +331 # - check that print-int32-buffered prints the hex textual representation +332 # setup +333 # . clear-stream(_test-stream) +334 # . . push args +335 68/push _test-stream/imm32 +336 # . . call +337 e8/call clear-stream/disp32 +338 # . . discard args +339 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +340 # . clear-stream(_test-buffered-file+4) +341 # . . push args +342 b8/copy-to-EAX _test-buffered-file/imm32 +343 05/add-to-EAX 4/imm32 +344 50/push-EAX +345 # . . call +346 e8/call clear-stream/disp32 +347 # . . discard args +348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +349 # print-int32-buffered(_test-buffered-file, 0x8899aa) +350 # . . push args +351 68/push 0x8899aa/imm32 +352 68/push _test-buffered-file/imm32 +353 # . . call +354 e8/call print-int32-buffered/disp32 +355 # . . discard args +356 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +357 # flush(_test-buffered-file) +358 # . . push args +359 68/push _test-buffered-file/imm32 +360 # . . call +361 e8/call flush/disp32 +362 # . . discard args +363 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +364 +-- 18 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- +382 # check-stream-equal(_test-stream, "0x008899aa", msg) +383 # . . push args +384 68/push "F - test-print-int32-buffered"/imm32 +385 68/push "0x008899aa"/imm32 +386 68/push _test-stream/imm32 +387 # . . call +388 e8/call check-stream-equal/disp32 +389 # . . discard args +390 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +391 # . end +392 c3/return +393 +394 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/068error-byte.subx.html b/html/subx/068error-byte.subx.html index b1a139a5..2fdb0eb5 100644 --- a/html/subx/068error-byte.subx.html +++ b/html/subx/068error-byte.subx.html @@ -3,8 +3,8 @@ Mu - subx/068error-byte.subx - - + + @@ -14,15 +14,15 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } -.subxS1Comment { color: #0000af; } .CommentedCode { color: #8a8a8a; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxFunction { color: #af5f00; text-decoration: underline; } .Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.SpecialChar { color: #d70000; } +.subxS2Comment { color: #8a8a8a; } --> @@ -39,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -93,7 +93,7 @@ if ('onhashchange' in window) { 33 68/push "Error: "/imm32 34 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 35 # . . call - 36 e8/call write-buffered/disp32 + 36 e8/call write-buffered/disp32 37 # . . discard args 38 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 39 # write-buffered(out, msg) @@ -101,7 +101,7 @@ if ('onhashchange' in window) { 41 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) 42 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 43 # . . call - 44 e8/call write-buffered/disp32 + 44 e8/call write-buffered/disp32 45 # . . discard args 46 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 47 # write-buffered(out, ": ") @@ -109,7 +109,7 @@ if ('onhashchange' in window) { 49 68/push ": "/imm32 50 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 51 # . . call - 52 e8/call write-buffered/disp32 + 52 e8/call write-buffered/disp32 53 # . . discard args 54 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 55 # print-byte-buffered(out, byte) @@ -117,7 +117,7 @@ if ('onhashchange' in window) { 57 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) 58 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 59 # . . call - 60 e8/call print-byte-buffered/disp32 + 60 e8/call print-byte-buffered/disp32 61 # . . discard args 62 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 63 # write-buffered(out, Newline) @@ -125,7 +125,7 @@ if ('onhashchange' in window) { 65 68/push Newline/imm32 66 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 67 # . . call - 68 e8/call write-buffered/disp32 + 68 e8/call write-buffered/disp32 69 # . . discard args 70 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 71 # . flush(out) @@ -140,7 +140,7 @@ if ('onhashchange' in window) { 80 68/push 1/imm32 81 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 82 # . . call - 83 e8/call stop/disp32 + 83 e8/call stop/disp32 84 # should never get past this point 85 $error-byte:dead-end: 86 # . epilog diff --git a/html/subx/069allocate.subx.html b/html/subx/069allocate.subx.html index 30bb3040..4d54b330 100644 --- a/html/subx/069allocate.subx.html +++ b/html/subx/069allocate.subx.html @@ -3,8 +3,8 @@ Mu - subx/069allocate.subx - - + + @@ -14,14 +14,14 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxTest { color: #5f8700; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/070new-stream.subx.html b/html/subx/070new-stream.subx.html index c99b6ab7..722715e4 100644 --- a/html/subx/070new-stream.subx.html +++ b/html/subx/070new-stream.subx.html @@ -3,8 +3,8 @@ Mu - subx/070new-stream.subx - - + + @@ -15,13 +15,13 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.LineNr { } .subxTest { color: #5f8700; } -.Constant { color: #008787; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -38,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/071read-line.subx.html b/html/subx/071read-line.subx.html index f2dd35b4..c407529b 100644 --- a/html/subx/071read-line.subx.html +++ b/html/subx/071read-line.subx.html @@ -3,8 +3,8 @@ Mu - subx/071read-line.subx - - + + @@ -14,16 +14,15 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } .subxS1Comment { color: #0000af; } -.CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.LineNr { } .subxTest { color: #5f8700; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } .subxS2Comment { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } --> @@ -40,7 +39,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -64,228 +63,221 @@ if ('onhashchange' in window) { 3 # . op subop mod rm32 base index scale r32 4 # . 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 5 - 6 #? Entry: # run a single test, while debugging - 7 #? e8/call test-read-line-buffered/disp32 - 8 #? # syscall(exit, Num-test-failures) - 9 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 10 #? b8/copy-to-EAX 1/imm32/exit - 11 #? cd/syscall 0x80/imm8 - 12 - 13 # read bytes from 'f' until (and including) a newline and store them into 's' - 14 # 's' fails to grow if and only if no data found - 15 # just abort if 's' is too small - 16 read-line-buffered: # f : (address buffered-file), s : (address stream byte) -> <void> - 17 # pseudocode: - 18 # while true - 19 # if (s->write >= s->length) abort - 20 # if (f->read >= f->write) populate stream from file - 21 # if (f->write == 0) break - 22 # AL = f->data[f->read] - 23 # s->data[s->write] = AL - 24 # ++f->read - 25 # ++s->write - 26 # if (AL == '\n') break - 27 # . prolog - 28 55/push-EBP - 29 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 30 # . save registers - 31 50/push-EAX - 32 51/push-ECX - 33 52/push-EDX - 34 56/push-ESI - 35 57/push-EDI - 36 # ESI = f - 37 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - 38 # ECX = f->read - 39 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX - 40 # EDI = s - 41 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - 42 # EDX = s->write - 43 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX - 44 $read-line-buffered:loop: - 45 # if (s->write >= s->length) abort - 46 3b/compare 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # compare EDX with *(EDI+8) - 47 7d/jump-if-greater-or-equal $read-line-buffered:abort/disp8 - 48 # if (f->read >= f->write) populate stream from file - 49 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) - 50 7c/jump-if-lesser $read-line-buffered:from-stream/disp8 - 51 # . clear-stream(stream = f+4) - 52 # . . push args - 53 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX - 54 50/push-EAX - 55 # . . call - 56 e8/call clear-stream/disp32 - 57 # . . discard args - 58 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 59 # . f->read must now be 0; update its cache at ECX - 60 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - 61 # . EAX = read(f->fd, stream = f+4) - 62 # . . push args - 63 50/push-EAX - 64 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - 65 # . . call - 66 e8/call read/disp32 - 67 # . . discard args - 68 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 69 # if (f->write == 0) break - 70 # since f->read was initially 0, EAX is the same as f->write - 71 # . if (EAX == 0) return true - 72 3d/compare-EAX-and 0/imm32 - 73 74/jump-if-equal $read-line-buffered:end/disp8 - 74 $read-line-buffered:from-stream: - 75 # AL = f->data[f->read] - 76 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 77 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy byte at *(ESI+ECX+16) to AL - 78 # s->data[s->write] = AL - 79 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EDX+12) - 80 # ++f->read - 81 41/increment-ECX - 82 # ++s->write - 83 42/increment-EDX - 84 # if (AL == '\n') return - 85 3d/compare-EAX-and 0xa/imm32 - 86 75/jump-if-not-equal $read-line-buffered:loop/disp8 - 87 $read-line-buffered:end: - 88 # save f->read - 89 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy ECX to *(ESI+8) - 90 # save s->write - 91 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI - 92 # . restore registers - 93 5f/pop-to-EDI - 94 5e/pop-to-ESI - 95 5a/pop-to-EDX - 96 59/pop-to-ECX - 97 58/pop-to-EAX - 98 # . epilog - 99 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -100 5d/pop-to-EBP -101 c3/return -102 -103 $read-line-buffered:abort: -104 # . _write(2/stderr, error) -105 # . . push args -106 68/push "read-line-buffered: line too long\n"/imm32 -107 68/push 2/imm32/stderr -108 # . . call -109 e8/call _write/disp32 -110 # . . discard args -111 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -112 # . syscall(exit, 1) -113 bb/copy-to-EBX 1/imm32 -114 b8/copy-to-EAX 1/imm32/exit -115 cd/syscall 0x80/imm8 -116 # never gets here -117 -118 test-read-line-buffered: -119 # - check that read-line-buffered stops at a newline -120 # setup -121 # . clear-stream(_test-stream) + 6 # read bytes from 'f' until (and including) a newline and store them into 's' + 7 # 's' fails to grow if and only if no data found + 8 # just abort if 's' is too small + 9 read-line-buffered: # f : (address buffered-file), s : (address stream byte) -> <void> + 10 # pseudocode: + 11 # while true + 12 # if (s->write >= s->length) abort + 13 # if (f->read >= f->write) populate stream from file + 14 # if (f->write == 0) break + 15 # AL = f->data[f->read] + 16 # s->data[s->write] = AL + 17 # ++f->read + 18 # ++s->write + 19 # if (AL == '\n') break + 20 # . prolog + 21 55/push-EBP + 22 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 23 # . save registers + 24 50/push-EAX + 25 51/push-ECX + 26 52/push-EDX + 27 56/push-ESI + 28 57/push-EDI + 29 # ESI = f + 30 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 31 # ECX = f->read + 32 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX + 33 # EDI = s + 34 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + 35 # EDX = s->write + 36 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX + 37 $read-line-buffered:loop: + 38 # if (s->write >= s->length) abort + 39 3b/compare 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # compare EDX with *(EDI+8) + 40 7d/jump-if-greater-or-equal $read-line-buffered:abort/disp8 + 41 # if (f->read >= f->write) populate stream from file + 42 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) + 43 7c/jump-if-lesser $read-line-buffered:from-stream/disp8 + 44 # . clear-stream(stream = f+4) + 45 # . . push args + 46 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX + 47 50/push-EAX + 48 # . . call + 49 e8/call clear-stream/disp32 + 50 # . . discard args + 51 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 52 # . f->read must now be 0; update its cache at ECX + 53 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX + 54 # . EAX = read(f->fd, stream = f+4) + 55 # . . push args + 56 50/push-EAX + 57 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI + 58 # . . call + 59 e8/call read/disp32 + 60 # . . discard args + 61 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 62 # if (f->write == 0) break + 63 # since f->read was initially 0, EAX is the same as f->write + 64 # . if (EAX == 0) return true + 65 3d/compare-EAX-and 0/imm32 + 66 74/jump-if-equal $read-line-buffered:end/disp8 + 67 $read-line-buffered:from-stream: + 68 # AL = f->data[f->read] + 69 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 70 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy byte at *(ESI+ECX+16) to AL + 71 # s->data[s->write] = AL + 72 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EDX+12) + 73 # ++f->read + 74 41/increment-ECX + 75 # ++s->write + 76 42/increment-EDX + 77 # if (AL == '\n') return + 78 3d/compare-EAX-and 0xa/imm32 + 79 75/jump-if-not-equal $read-line-buffered:loop/disp8 + 80 $read-line-buffered:end: + 81 # save f->read + 82 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy ECX to *(ESI+8) + 83 # save s->write + 84 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI + 85 # . restore registers + 86 5f/pop-to-EDI + 87 5e/pop-to-ESI + 88 5a/pop-to-EDX + 89 59/pop-to-ECX + 90 58/pop-to-EAX + 91 # . epilog + 92 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 93 5d/pop-to-EBP + 94 c3/return + 95 + 96 $read-line-buffered:abort: + 97 # . _write(2/stderr, error) + 98 # . . push args + 99 68/push "read-line-buffered: line too long\n"/imm32 +100 68/push 2/imm32/stderr +101 # . . call +102 e8/call _write/disp32 +103 # . . discard args +104 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +105 # . syscall(exit, 1) +106 bb/copy-to-EBX 1/imm32 +107 b8/copy-to-EAX 1/imm32/exit +108 cd/syscall 0x80/imm8 +109 # never gets here +110 +111 test-read-line-buffered: +112 # - check that read-line-buffered stops at a newline +113 # setup +114 # . clear-stream(_test-stream) +115 # . . push args +116 68/push _test-stream/imm32 +117 # . . call +118 e8/call clear-stream/disp32 +119 # . . discard args +120 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +121 # . clear-stream(_test-buffered-file+4) 122 # . . push args -123 68/push _test-stream/imm32 -124 # . . call -125 e8/call clear-stream/disp32 -126 # . . discard args -127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -128 # . clear-stream(_test-buffered-file+4) -129 # . . push args -130 b8/copy-to-EAX _test-buffered-file/imm32 -131 05/add-to-EAX 4/imm32 -132 50/push-EAX +123 b8/copy-to-EAX _test-buffered-file/imm32 +124 05/add-to-EAX 4/imm32 +125 50/push-EAX +126 # . . call +127 e8/call clear-stream/disp32 +128 # . . discard args +129 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +130 # . clear-stream(_test-tmp-stream) +131 # . . push args +132 68/push _test-tmp-stream/imm32 133 # . . call 134 e8/call clear-stream/disp32 135 # . . discard args 136 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -137 # . clear-stream(_test-tmp-stream) +137 # write(_test-stream, "ab\ncd") 138 # . . push args -139 68/push _test-tmp-stream/imm32 -140 # . . call -141 e8/call clear-stream/disp32 -142 # . . discard args -143 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -144 # write(_test-stream, "ab\ncd") -145 # . . push args -146 68/push "ab\ncd"/imm32 -147 68/push _test-stream/imm32 -148 # . . call -149 e8/call write/disp32 -150 # . . discard args -151 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -152 # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream -153 # . EAX = read-line-buffered(_test-buffered-file, _test-tmp-stream) -154 # . . push args -155 68/push _test-tmp-stream/imm32 -156 68/push _test-buffered-file/imm32 -157 # . . call -158 e8/call read-line-buffered/disp32 -159 # . . discard args -160 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -161 # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) -162 # . . push args -163 68/push "F - test-read-line-buffered"/imm32 -164 68/push "ab"/imm32 -165 68/push _test-tmp-stream/imm32 -166 # . . call -167 e8/call check-next-stream-line-equal/disp32 -168 # . . discard args -169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -170 # end -171 c3/return -172 -173 test-read-line-buffered-reads-final-line-until-Eof: -174 # setup -175 # . clear-stream(_test-stream) +139 68/push "ab\ncd"/imm32 +140 68/push _test-stream/imm32 +141 # . . call +142 e8/call write/disp32 +143 # . . discard args +144 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +145 # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream +146 # . EAX = read-line-buffered(_test-buffered-file, _test-tmp-stream) +147 # . . push args +148 68/push _test-tmp-stream/imm32 +149 68/push _test-buffered-file/imm32 +150 # . . call +151 e8/call read-line-buffered/disp32 +152 # . . discard args +153 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +154 # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) +155 # . . push args +156 68/push "F - test-read-line-buffered"/imm32 +157 68/push "ab"/imm32 +158 68/push _test-tmp-stream/imm32 +159 # . . call +160 e8/call check-next-stream-line-equal/disp32 +161 # . . discard args +162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +163 # end +164 c3/return +165 +166 test-read-line-buffered-reads-final-line-until-Eof: +167 # setup +168 # . clear-stream(_test-stream) +169 # . . push args +170 68/push _test-stream/imm32 +171 # . . call +172 e8/call clear-stream/disp32 +173 # . . discard args +174 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +175 # . clear-stream(_test-buffered-file+4) 176 # . . push args -177 68/push _test-stream/imm32 -178 # . . call -179 e8/call clear-stream/disp32 -180 # . . discard args -181 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -182 # . clear-stream(_test-buffered-file+4) -183 # . . push args -184 b8/copy-to-EAX _test-buffered-file/imm32 -185 05/add-to-EAX 4/imm32 -186 50/push-EAX +177 b8/copy-to-EAX _test-buffered-file/imm32 +178 05/add-to-EAX 4/imm32 +179 50/push-EAX +180 # . . call +181 e8/call clear-stream/disp32 +182 # . . discard args +183 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +184 # . clear-stream(_test-tmp-stream) +185 # . . push args +186 68/push _test-tmp-stream/imm32 187 # . . call 188 e8/call clear-stream/disp32 189 # . . discard args 190 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -191 # . clear-stream(_test-tmp-stream) +191 # write(_test-stream, "cd") 192 # . . push args -193 68/push _test-tmp-stream/imm32 -194 # . . call -195 e8/call clear-stream/disp32 -196 # . . discard args -197 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -198 # write(_test-stream, "cd") -199 # . . push args -200 68/push "cd"/imm32 -201 68/push _test-stream/imm32 -202 # . . call -203 e8/call write/disp32 -204 # . . discard args -205 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -206 # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream -207 # . EAX = read-line-buffered(_test-buffered-file, _test-tmp-stream) -208 # . . push args -209 68/push _test-tmp-stream/imm32 -210 68/push _test-buffered-file/imm32 -211 # . . call -212 e8/call read-line-buffered/disp32 -213 # . . discard args -214 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -215 # check-stream-equal(_test-tmp-stream, "cd", msg) -216 # . . push args -217 68/push "F - test-read-line-buffered-reads-final-line-until-Eof"/imm32 -218 68/push "cd"/imm32 -219 68/push _test-tmp-stream/imm32 -220 # . . call -221 e8/call check-stream-equal/disp32 -222 # . . discard args -223 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -224 # end -225 c3/return -226 -227 # . . vim:nowrap:textwidth=0 +193 68/push "cd"/imm32 +194 68/push _test-stream/imm32 +195 # . . call +196 e8/call write/disp32 +197 # . . discard args +198 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +199 # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream +200 # . EAX = read-line-buffered(_test-buffered-file, _test-tmp-stream) +201 # . . push args +202 68/push _test-tmp-stream/imm32 +203 68/push _test-buffered-file/imm32 +204 # . . call +205 e8/call read-line-buffered/disp32 +206 # . . discard args +207 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +208 # check-stream-equal(_test-tmp-stream, "cd", msg) +209 # . . push args +210 68/push "F - test-read-line-buffered-reads-final-line-until-Eof"/imm32 +211 68/push "cd"/imm32 +212 68/push _test-tmp-stream/imm32 +213 # . . call +214 e8/call check-stream-equal/disp32 +215 # . . discard args +216 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +217 # end +218 c3/return +219 +220 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/072slice.subx.html b/html/subx/072slice.subx.html index 0d40c1ce..2e601bcf 100644 --- a/html/subx/072slice.subx.html +++ b/html/subx/072slice.subx.html @@ -3,8 +3,8 @@ Mu - subx/072slice.subx - - + + @@ -14,18 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.Constant { color: #008787; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Folded { color: #080808; background-color: #949494; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Folded { color: #080808; background-color: #949494; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxTest { color: #5f8700; } --> @@ -42,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -69,977 +67,1146 @@ if ('onhashchange' in window) { 6 # . op subop mod rm32 base index scale r32 7 # . 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 8 - 9 #? Entry: # run a single test, while debugging - 10 #? e8/call test-slice-to-string/disp32 - 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 #? b8/copy-to-EAX 1/imm32/exit - 14 #? cd/syscall 0x80/imm8 - 15 - 16 slice-empty?: # s : (address slice) -> EAX : boolean - 17 # . prolog - 18 55/push-EBP - 19 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 20 # . save registers - 21 51/push-ECX - 22 # ECX = s - 23 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - 24 # if (s->start == s->end) return true - 25 # . EAX = s->start - 26 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 27 # . compare EAX and s->end - 28 39/compare 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # compare EAX and *(ECX+4) - 29 b8/copy-to-EAX 1/imm32/true - 30 74/jump-if-equal $slice-empty?:end/disp8 - 31 b8/copy-to-EAX 0/imm32/false - 32 $slice-empty?:end: - 33 # . restore registers - 34 59/pop-to-ECX - 35 # . epilog - 36 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 37 5d/pop-to-EBP - 38 c3/return - 39 - 40 test-slice-empty-true: - 41 # . prolog - 42 55/push-EBP - 43 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 44 # var slice/ECX = {34, 34} - 45 68/push 34/imm32/end - 46 68/push 34/imm32/start - 47 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 48 # slice-empty?(slice) + 9 slice-empty?: # s : (address slice) -> EAX : boolean + 10 # . prolog + 11 55/push-EBP + 12 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 13 # . save registers + 14 51/push-ECX + 15 # ECX = s + 16 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + 17 # if (s->start == s->end) return true + 18 # . EAX = s->start + 19 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX + 20 # . compare EAX and s->end + 21 39/compare 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # compare EAX and *(ECX+4) + 22 b8/copy-to-EAX 1/imm32/true + 23 74/jump-if-equal $slice-empty?:end/disp8 + 24 b8/copy-to-EAX 0/imm32/false + 25 $slice-empty?:end: + 26 # . restore registers + 27 59/pop-to-ECX + 28 # . epilog + 29 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 30 5d/pop-to-EBP + 31 c3/return + 32 + 33 test-slice-empty-true: + 34 # . prolog + 35 55/push-EBP + 36 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 37 # var slice/ECX = {34, 34} + 38 68/push 34/imm32/end + 39 68/push 34/imm32/start + 40 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 41 # slice-empty?(slice) + 42 # . . push args + 43 51/push-ECX + 44 # . . call + 45 e8/call slice-empty?/disp32 + 46 # . . discard args + 47 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 48 # check-ints-equal(EAX, 1, msg) 49 # . . push args - 50 51/push-ECX - 51 # . . call - 52 e8/call slice-empty?/disp32 - 53 # . . discard args - 54 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 55 # check-ints-equal(EAX, 1, msg) - 56 # . . push args - 57 68/push "F - test-slice-empty-true"/imm32 - 58 68/push 1/imm32 - 59 50/push-EAX - 60 # . . call - 61 e8/call check-ints-equal/disp32 - 62 # . . discard args - 63 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 64 # . epilog - 65 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 66 5d/pop-to-EBP - 67 c3/return - 68 - 69 test-slice-empty-false: - 70 # . prolog - 71 55/push-EBP - 72 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 73 # var slice/ECX = {34, 23} - 74 68/push 23/imm32/end - 75 68/push 34/imm32/start - 76 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 77 # slice-empty?(slice) + 50 68/push "F - test-slice-empty-true"/imm32 + 51 68/push 1/imm32 + 52 50/push-EAX + 53 # . . call + 54 e8/call check-ints-equal/disp32 + 55 # . . discard args + 56 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 57 # . epilog + 58 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 59 5d/pop-to-EBP + 60 c3/return + 61 + 62 test-slice-empty-false: + 63 # . prolog + 64 55/push-EBP + 65 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 66 # var slice/ECX = {34, 23} + 67 68/push 23/imm32/end + 68 68/push 34/imm32/start + 69 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 70 # slice-empty?(slice) + 71 # . . push args + 72 51/push-ECX + 73 # . . call + 74 e8/call slice-empty?/disp32 + 75 # . . discard args + 76 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 77 # check-ints-equal(EAX, 0, msg) 78 # . . push args - 79 51/push-ECX - 80 # . . call - 81 e8/call slice-empty?/disp32 - 82 # . . discard args - 83 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 84 # check-ints-equal(EAX, 0, msg) - 85 # . . push args - 86 68/push "F - test-slice-empty-false"/imm32 - 87 68/push 0/imm32 - 88 50/push-EAX - 89 # . . call - 90 e8/call check-ints-equal/disp32 - 91 # . . discard args - 92 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 93 # . epilog - 94 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 95 5d/pop-to-EBP - 96 c3/return - 97 - 98 slice-equal?: # s : (address slice), p : (address string) -> EAX : boolean - 99 # pseudocode: - 100 # if (p == 0) return (s == 0) - 101 # currs = s->start - 102 # maxs = s->end - 103 # if (maxs - currs != p->length) return false - 104 # currp = p->data - 105 # while currs < maxs - 106 # if (*currs != *currp) return false - 107 # ++currs - 108 # ++currp - 109 # return true + 79 68/push "F - test-slice-empty-false"/imm32 + 80 68/push 0/imm32 + 81 50/push-EAX + 82 # . . call + 83 e8/call check-ints-equal/disp32 + 84 # . . discard args + 85 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 86 # . epilog + 87 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 88 5d/pop-to-EBP + 89 c3/return + 90 + 91 slice-equal?: # s : (address slice), p : (address string) -> EAX : boolean + 92 # pseudocode: + 93 # if (p == 0) return (s == 0) + 94 # currs = s->start + 95 # maxs = s->end + 96 # if (maxs - currs != p->length) return false + 97 # currp = p->data + 98 # while currs < maxs + 99 # if (*currs != *currp) return false + 100 # ++currs + 101 # ++currp + 102 # return true + 103 # + 104 # registers: + 105 # currs: EDX + 106 # maxs: ESI + 107 # currp: EBX + 108 # *currs: EAX + 109 # *currp: ECX 110 # - 111 # registers: - 112 # currs: EDX - 113 # maxs: ESI - 114 # currp: EBX - 115 # *currs: EAX - 116 # *currp: ECX - 117 # - 118 # . prolog - 119 55/push-EBP - 120 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 121 # . save registers - 122 51/push-ECX - 123 52/push-EDX - 124 53/push-EBX - 125 56/push-ESI - 126 # ESI = s - 127 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - 128 # currs/EDX = s->start - 129 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 130 # maxs/ESI = s->end - 131 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI - 132 # EAX = maxs - currs - 133 89/copy 3/mod/direct 0/rm32/EAX . . . 6/r32/ESI . . # copy ESI to EAX - 134 29/subtract 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # subtract EDX from EAX - 135 # EBX = p - 136 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - 137 # if (p != 0) goto next check - 138 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 139 75/jump-if-not-equal $slice-equal?:nonnull-string/disp8 - 140 $slice-equal?:null-string: - 141 # return s->start == s->end - 142 3d/compare-EAX-and 0/imm32 - 143 74/jump-if-equal $slice-equal?:true/disp8 - 144 eb/jump $slice-equal?:false/disp8 - 145 $slice-equal?:nonnull-string: - 146 # if (EAX != p->length) return false - 147 39/compare 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # compare *EBX and EAX - 148 75/jump-if-not-equal $slice-equal?:false/disp8 - 149 # currp/EBX = p->data - 150 81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX - 151 # EAX = ECX = 0 - 152 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 153 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - 154 $slice-equal?:loop: - 155 # if (currs >= maxs) return true - 156 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI - 157 7d/jump-if-greater-or-equal $slice-equal?:true/disp8 - 158 # AL = *currp - 159 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL - 160 # CL = *currs - 161 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL - 162 # if (EAX != ECX) return false - 163 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX and ECX - 164 75/jump-if-not-equal $slice-equal?:false/disp8 - 165 # ++currp - 166 43/increment-EBX - 167 # ++currs - 168 42/increment-EDX - 169 eb/jump $slice-equal?:loop/disp8 - 170 $slice-equal?:false: - 171 b8/copy-to-EAX 0/imm32 - 172 eb/jump $slice-equal?:end/disp8 - 173 $slice-equal?:true: - 174 b8/copy-to-EAX 1/imm32 - 175 $slice-equal?:end: - 176 # . restore registers - 177 5e/pop-to-ESI - 178 5b/pop-to-EBX - 179 5a/pop-to-EDX - 180 59/pop-to-ECX - 181 # . epilog - 182 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 183 5d/pop-to-EBP - 184 c3/return - 185 - 186 test-slice-equal: - 187 # - slice-equal?(slice("Abc"), "Abc") == 1 - 188 # . prolog - 189 55/push-EBP - 190 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 191 # var slice/ECX - 192 68/push _test-slice-data-3/imm32/end - 193 68/push _test-slice-data-0/imm32/start - 194 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 195 # EAX = slice-equal?(ECX, "Abc") - 196 # . . push args - 197 68/push "Abc"/imm32 - 198 51/push-ECX - 199 # . . call - 200 e8/call slice-equal?/disp32 - 201 # . . discard args - 202 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 203 # check-ints-equal(EAX, 1, msg) - 204 # . . push args - 205 68/push "F - test-slice-equal"/imm32 - 206 68/push 1/imm32 - 207 50/push-EAX - 208 # . . call - 209 e8/call check-ints-equal/disp32 - 210 # . . discard args - 211 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 212 # . epilog - 213 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 214 5d/pop-to-EBP - 215 c3/return - 216 - 217 test-slice-equal-false: - 218 # - slice-equal?(slice("bcd"), "Abc") == 0 - 219 # . prolog - 220 55/push-EBP - 221 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 222 # var slice/ECX - 223 68/push _test-slice-data-4/imm32/end - 224 68/push _test-slice-data-1/imm32/start - 225 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 226 # EAX = slice-equal?(ECX, "Abc") - 227 # . . push args - 228 68/push "Abc"/imm32 - 229 51/push-ECX - 230 # . . call - 231 e8/call slice-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, 0, msg) - 235 # . . push args - 236 68/push "F - test-slice-equal-false"/imm32 - 237 68/push 0/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 # . epilog - 244 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 245 5d/pop-to-EBP - 246 c3/return - 247 - 248 test-slice-equal-too-long: - 249 # - slice-equal?(slice("Abcd"), "Abc") == 0 - 250 # . prolog - 251 55/push-EBP - 252 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 253 # var slice/ECX - 254 68/push _test-slice-data-4/imm32/end - 255 68/push _test-slice-data-0/imm32/start - 256 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 257 # EAX = slice-equal?(ECX, "Abc") - 258 # . . push args - 259 68/push "Abc"/imm32 - 260 51/push-ECX - 261 # . . call - 262 e8/call slice-equal?/disp32 - 263 # . . discard args - 264 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 265 # check-ints-equal(EAX, 0, msg) + 111 # . prolog + 112 55/push-EBP + 113 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 114 # . save registers + 115 51/push-ECX + 116 52/push-EDX + 117 53/push-EBX + 118 56/push-ESI + 119 # ESI = s + 120 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 121 # currs/EDX = s->start + 122 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX + 123 # maxs/ESI = s->end + 124 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI + 125 # EAX = maxs - currs + 126 89/copy 3/mod/direct 0/rm32/EAX . . . 6/r32/ESI . . # copy ESI to EAX + 127 29/subtract 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # subtract EDX from EAX + 128 # EBX = p + 129 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX + 130 # if (p != 0) goto next check + 131 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX + 132 75/jump-if-not-equal $slice-equal?:nonnull-string/disp8 + 133 $slice-equal?:null-string: + 134 # return s->start == s->end + 135 3d/compare-EAX-and 0/imm32 + 136 74/jump-if-equal $slice-equal?:true/disp8 + 137 eb/jump $slice-equal?:false/disp8 + 138 $slice-equal?:nonnull-string: + 139 # if (EAX != p->length) return false + 140 39/compare 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # compare *EBX and EAX + 141 75/jump-if-not-equal $slice-equal?:false/disp8 + 142 # currp/EBX = p->data + 143 81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX + 144 # EAX = ECX = 0 + 145 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 146 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX + 147 $slice-equal?:loop: + 148 # if (currs >= maxs) return true + 149 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI + 150 73/jump-if-greater-or-equal-unsigned $slice-equal?:true/disp8 + 151 # AL = *currp + 152 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL + 153 # CL = *currs + 154 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL + 155 # if (EAX != ECX) return false + 156 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX and ECX + 157 75/jump-if-not-equal $slice-equal?:false/disp8 + 158 # ++currp + 159 43/increment-EBX + 160 # ++currs + 161 42/increment-EDX + 162 eb/jump $slice-equal?:loop/disp8 + 163 $slice-equal?:false: + 164 b8/copy-to-EAX 0/imm32 + 165 eb/jump $slice-equal?:end/disp8 + 166 $slice-equal?:true: + 167 b8/copy-to-EAX 1/imm32 + 168 $slice-equal?:end: + 169 # . restore registers + 170 5e/pop-to-ESI + 171 5b/pop-to-EBX + 172 5a/pop-to-EDX + 173 59/pop-to-ECX + 174 # . epilog + 175 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 176 5d/pop-to-EBP + 177 c3/return + 178 + 179 test-slice-equal: + 180 # - slice-equal?(slice("Abc"), "Abc") == 1 + 181 # . prolog + 182 55/push-EBP + 183 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 184 # (EAX..ECX) = "Abc" + 185 b8/copy-to-EAX "Abc"/imm32 + 186 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 187 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 188 05/add-to-EAX 4/imm32 + 189 # var slice/ECX = {EAX, ECX} + 190 51/push-ECX + 191 50/push-EAX + 192 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 193 # EAX = slice-equal?(ECX, "Abc") + 194 # . . push args + 195 68/push "Abc"/imm32 + 196 51/push-ECX + 197 # . . call + 198 e8/call slice-equal?/disp32 + 199 # . . discard args + 200 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 201 # check-ints-equal(EAX, 1, msg) + 202 # . . push args + 203 68/push "F - test-slice-equal"/imm32 + 204 68/push 1/imm32 + 205 50/push-EAX + 206 # . . call + 207 e8/call check-ints-equal/disp32 + 208 # . . discard args + 209 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 210 # . epilog + 211 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 212 5d/pop-to-EBP + 213 c3/return + 214 + 215 test-slice-equal-false: + 216 # - slice-equal?(slice("bcd"), "Abc") == 0 + 217 # . prolog + 218 55/push-EBP + 219 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 220 # (EAX..ECX) = "bcd" + 221 b8/copy-to-EAX "bcd"/imm32 + 222 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 223 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 224 05/add-to-EAX 4/imm32 + 225 # var slice/ECX = {EAX, ECX} + 226 51/push-ECX + 227 50/push-EAX + 228 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 229 # EAX = slice-equal?(ECX, "Abc") + 230 # . . push args + 231 68/push "Abc"/imm32 + 232 51/push-ECX + 233 # . . call + 234 e8/call slice-equal?/disp32 + 235 # . . discard args + 236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 237 # check-ints-equal(EAX, 0, msg) + 238 # . . push args + 239 68/push "F - test-slice-equal-false"/imm32 + 240 68/push 0/imm32 + 241 50/push-EAX + 242 # . . call + 243 e8/call check-ints-equal/disp32 + 244 # . . discard args + 245 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 246 # . epilog + 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-slice-equal-too-long: + 252 # - slice-equal?(slice("Abcd"), "Abc") == 0 + 253 # . prolog + 254 55/push-EBP + 255 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 256 # (EAX..ECX) = "Abcd" + 257 b8/copy-to-EAX "Abcd"/imm32 + 258 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 259 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 260 05/add-to-EAX 4/imm32 + 261 # var slice/ECX = {EAX, ECX} + 262 51/push-ECX + 263 50/push-EAX + 264 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 265 # EAX = slice-equal?(ECX, "Abc") 266 # . . push args - 267 68/push "F - test-slice-equal-too-long"/imm32 - 268 68/push 0/imm32 - 269 50/push-EAX - 270 # . . call - 271 e8/call check-ints-equal/disp32 - 272 # . . discard args - 273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 274 # . epilog - 275 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 276 5d/pop-to-EBP - 277 c3/return - 278 - 279 test-slice-equal-too-short: - 280 # - slice-equal?(slice("A"), "Abc") == 0 - 281 # . prolog - 282 55/push-EBP - 283 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 284 # var slice/ECX - 285 68/push _test-slice-data-1/imm32/end - 286 68/push _test-slice-data-0/imm32/start - 287 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 288 # EAX = slice-equal?(ECX, "Abc") - 289 # . . push args - 290 68/push "Abc"/imm32 - 291 51/push-ECX - 292 # . . call - 293 e8/call slice-equal?/disp32 - 294 # . . discard args - 295 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 296 # check-ints-equal(EAX, 0, msg) - 297 # . . push args - 298 68/push "F - test-slice-equal-too-short"/imm32 - 299 68/push 0/imm32 - 300 50/push-EAX - 301 # . . call - 302 e8/call check-ints-equal/disp32 - 303 # . . discard args - 304 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 305 # . epilog - 306 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 307 5d/pop-to-EBP - 308 c3/return - 309 - 310 test-slice-equal-empty: - 311 # - slice-equal?(slice(""), "Abc") == 0 - 312 # . prolog - 313 55/push-EBP - 314 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 315 # var slice/ECX - 316 68/push _test-slice-data-0/imm32/end - 317 68/push _test-slice-data-0/imm32/start - 318 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 319 # EAX = slice-equal?(ECX, "Abc") - 320 # . . push args - 321 68/push "Abc"/imm32 - 322 51/push-ECX - 323 # . . call - 324 e8/call slice-equal?/disp32 - 325 # . . discard args - 326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 327 # check-ints-equal(EAX, 0, msg) - 328 # . . push args - 329 68/push "F - test-slice-equal-empty"/imm32 - 330 68/push 0/imm32 - 331 50/push-EAX - 332 # . . call - 333 e8/call check-ints-equal/disp32 - 334 # . . discard args - 335 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 336 # . epilog - 337 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 338 5d/pop-to-EBP - 339 c3/return - 340 - 341 test-slice-equal-with-empty: - 342 # - slice-equal?(slice("Ab"), "") == 0 - 343 # . prolog - 344 55/push-EBP - 345 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 346 # var slice/ECX - 347 68/push _test-slice-data-2/imm32/end - 348 68/push _test-slice-data-0/imm32/start - 349 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 350 # EAX = slice-equal?(ECX, "") - 351 # . . push args - 352 68/push ""/imm32 - 353 51/push-ECX - 354 # . . call - 355 e8/call slice-equal?/disp32 - 356 # . . discard args - 357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 358 # check-ints-equal(EAX, 0, msg) - 359 # . . push args - 360 68/push "F - test-slice-equal-with-empty"/imm32 - 361 68/push 0/imm32 - 362 50/push-EAX - 363 # . . call - 364 e8/call check-ints-equal/disp32 - 365 # . . discard args - 366 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 367 # . epilog - 368 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 369 5d/pop-to-EBP - 370 c3/return - 371 - 372 test-slice-equal-empty-with-empty: - 373 # - slice-equal?(slice(""), "") == 1 - 374 # . prolog - 375 55/push-EBP - 376 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 377 # var slice/ECX - 378 68/push _test-slice-data-0/imm32/end - 379 68/push _test-slice-data-0/imm32/start - 380 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 381 # EAX = slice-equal?(ECX, "") - 382 # . . push args - 383 68/push ""/imm32 - 384 51/push-ECX - 385 # . . call - 386 e8/call slice-equal?/disp32 - 387 # . . discard args - 388 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 389 # check-ints-equal(EAX, 1, msg) - 390 # . . push args - 391 68/push "F - test-slice-equal-empty-with-empty"/imm32 - 392 68/push 1/imm32 - 393 50/push-EAX - 394 # . . call - 395 e8/call check-ints-equal/disp32 - 396 # . . discard args - 397 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 398 # . epilog - 399 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 400 5d/pop-to-EBP - 401 c3/return - 402 - 403 test-slice-equal-with-null: - 404 # - slice-equal?(slice("Ab"), null) == 0 - 405 # . prolog - 406 55/push-EBP - 407 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 408 # var slice/ECX - 409 68/push _test-slice-data-2/imm32/end - 410 68/push _test-slice-data-0/imm32/start - 411 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 412 # EAX = slice-equal?(ECX, 0) - 413 # . . push args - 414 68/push 0/imm32 - 415 51/push-ECX - 416 # . . call - 417 e8/call slice-equal?/disp32 - 418 # . . discard args - 419 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 420 # check-ints-equal(EAX, 0, msg) - 421 # . . push args - 422 68/push "F - test-slice-equal-with-null"/imm32 - 423 68/push 0/imm32 - 424 50/push-EAX - 425 # . . call - 426 e8/call check-ints-equal/disp32 - 427 # . . discard args - 428 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 429 # . epilog - 430 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 431 5d/pop-to-EBP - 432 c3/return - 433 - 434 slice-starts-with?: # s : (address slice), head : (address string) -> EAX : boolean - 435 # pseudocode - 436 # lenh = head->length - 437 # if (lenh > s->end - s->start) return false - 438 # i = 0 - 439 # currs = s->start - 440 # currp = head->data - 441 # while i < lenh - 442 # if (*currs != *currh) return false - 443 # ++i - 444 # ++currs - 445 # ++currh - 446 # return true - 447 # - 448 # registers: - 449 # currs: ESI - 450 # currh: EDI - 451 # *currs: EAX - 452 # *currh: EBX - 453 # i: ECX - 454 # lenh: EDX - 455 # - 456 # . prolog - 457 55/push-EBP - 458 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 459 # . save registers - 460 51/push-ECX - 461 52/push-EDX - 462 53/push-EBX - 463 56/push-ESI - 464 57/push-EDI - 465 # ESI = s - 466 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - 467 # ECX = s->end - s->start - 468 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 469 2b/subtract 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # subtract *ESI from ECX - 470 # EDI = head - 471 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - 472 # lenh/EDX = head->length - 473 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX - 474 # if (lenh > s->end - s->start) return false - 475 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX - 476 7f/jump-if-greater $slice-starts-with?:false/disp8 - 477 # currs/ESI = s->start - 478 8b/subtract 0/mod/indirect 6/rm32/ESI . . . 6/r32/ESI . . # copy *ESI to ESI - 479 # currh/EDI = head->data - 480 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - 481 # i/ECX = 0 - 482 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - 483 # EAX = EBX = 0 - 484 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 485 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 486 $slice-starts-with?:loop: - 487 # if (i >= lenh) return true - 488 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 489 7d/jump-if-greater-or-equal $slice-starts-with?:true/disp8 - 490 # AL = *currs - 491 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - 492 # BL = *currh - 493 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at *EDI to BL - 494 # if (*currs != *currh) return false - 495 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 496 75/jump-if-not-equal $slice-starts-with?:false/disp8 - 497 # ++i - 498 41/increment-ECX - 499 # ++currs - 500 46/increment-ESI - 501 # ++currh - 502 47/increment-EDI - 503 eb/jump $slice-starts-with?:loop/disp8 - 504 $slice-starts-with?:true: - 505 b8/copy-to-EAX 1/imm32 - 506 eb/jump $slice-starts-with?:end/disp8 - 507 $slice-starts-with?:false: - 508 b8/copy-to-EAX 0/imm32 - 509 $slice-starts-with?:end: - 510 # . restore registers - 511 5f/pop-to-EDI - 512 5e/pop-to-ESI - 513 5b/pop-to-EBX - 514 5a/pop-to-EDX - 515 59/pop-to-ECX - 516 # . epilog - 517 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 518 5d/pop-to-EBP - 519 c3/return - 520 - 521 test-slice-starts-with-single-character: - 522 # - slice-starts-with?(slice("Abc"), "A") == 1 - 523 # . prolog - 524 55/push-EBP - 525 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 526 # var slice/ECX - 527 68/push _test-slice-data-3/imm32/end - 528 68/push _test-slice-data-0/imm32/start - 529 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 530 # EAX = slice-starts-with?(ECX, "A") - 531 # . . push args - 532 68/push "A"/imm32 - 533 51/push-ECX - 534 # . . call - 535 e8/call slice-starts-with?/disp32 - 536 # . . discard args - 537 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 538 # check-ints-equal(EAX, 1, msg) - 539 # . . push args - 540 68/push "F - test-slice-starts-with-single-character"/imm32 - 541 68/push 1/imm32 - 542 50/push-EAX - 543 # . . call - 544 e8/call check-ints-equal/disp32 - 545 # . . discard args - 546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 547 # . epilog - 548 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 549 5d/pop-to-EBP - 550 c3/return - 551 - 552 test-slice-starts-with-empty-string: - 553 # - slice-starts-with?(slice("Abc"), "") == 1 - 554 # . prolog - 555 55/push-EBP - 556 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 557 # var slice/ECX - 558 68/push _test-slice-data-3/imm32/end - 559 68/push _test-slice-data-0/imm32/start - 560 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 561 # EAX = slice-starts-with?(ECX, "") - 562 # . . push args - 563 68/push ""/imm32 - 564 51/push-ECX - 565 # . . call - 566 e8/call slice-starts-with?/disp32 - 567 # . . discard args - 568 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 569 # check-ints-equal(EAX, 1, msg) - 570 # . . push args - 571 68/push "F - test-slice-starts-with-empty-string"/imm32 - 572 68/push 1/imm32 - 573 50/push-EAX - 574 # . . call - 575 e8/call check-ints-equal/disp32 - 576 # . . discard args - 577 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 578 # . epilog - 579 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 580 5d/pop-to-EBP - 581 c3/return - 582 - 583 test-slice-starts-with-multiple-characters: - 584 # - slice-starts-with?(slice("Abc"), "Ab") == 1 - 585 # . prolog - 586 55/push-EBP - 587 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 588 # var slice/ECX - 589 68/push _test-slice-data-3/imm32/end - 590 68/push _test-slice-data-0/imm32/start - 591 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 592 # EAX = slice-starts-with?(ECX, "Ab") - 593 # . . push args - 594 68/push "Ab"/imm32 - 595 51/push-ECX - 596 # . . call - 597 e8/call slice-starts-with?/disp32 - 598 # . . discard args - 599 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 600 # check-ints-equal(EAX, 1, msg) - 601 # . . push args - 602 68/push "F - test-slice-starts-with-multiple-characters"/imm32 - 603 68/push 1/imm32 - 604 50/push-EAX - 605 # . . call - 606 e8/call check-ints-equal/disp32 - 607 # . . discard args - 608 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 609 # . epilog - 610 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 611 5d/pop-to-EBP - 612 c3/return - 613 - 614 test-slice-starts-with-entire-string: - 615 # - slice-starts-with?(slice("Abc"), "Abc") == 1 - 616 # . prolog - 617 55/push-EBP - 618 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 619 # var slice/ECX - 620 68/push _test-slice-data-3/imm32/end - 621 68/push _test-slice-data-0/imm32/start - 622 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 623 # EAX = slice-starts-with?(ECX, "Abc") - 624 # . . push args - 625 68/push "Abc"/imm32 - 626 51/push-ECX - 627 # . . call - 628 e8/call slice-starts-with?/disp32 - 629 # . . discard args - 630 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 631 # check-ints-equal(EAX, 1, msg) - 632 # . . push args - 633 68/push "F - test-slice-starts-with-entire-string"/imm32 - 634 68/push 1/imm32 - 635 50/push-EAX - 636 # . . call - 637 e8/call check-ints-equal/disp32 - 638 # . . discard args - 639 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 640 # . epilog - 641 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 642 5d/pop-to-EBP - 643 c3/return - 644 - 645 test-slice-starts-with-fails: - 646 # - slice-starts-with?(slice("Abc"), "Abd") == 1 - 647 # . prolog - 648 55/push-EBP - 649 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 650 # var slice/ECX - 651 68/push _test-slice-data-3/imm32/end - 652 68/push _test-slice-data-0/imm32/start - 653 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 654 # EAX = slice-starts-with?(ECX, "Abd") - 655 # . . push args - 656 68/push "Abd"/imm32 - 657 51/push-ECX - 658 # . . call - 659 e8/call slice-starts-with?/disp32 - 660 # . . discard args - 661 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 662 # check-ints-equal(EAX, 0, msg) - 663 # . . push args - 664 68/push "F - test-slice-starts-with-fails"/imm32 - 665 68/push 0/imm32 - 666 50/push-EAX - 667 # . . call - 668 e8/call check-ints-equal/disp32 - 669 # . . discard args - 670 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 671 # . epilog - 672 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 673 5d/pop-to-EBP - 674 c3/return - 675 - 676 test-slice-starts-with-fails-2: - 677 # - slice-starts-with?(slice("Abc"), "Ac") == 1 - 678 # . prolog - 679 55/push-EBP - 680 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 681 # var slice/ECX - 682 68/push _test-slice-data-3/imm32/end - 683 68/push _test-slice-data-0/imm32/start - 684 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 685 # EAX = slice-starts-with?(ECX, "Ac") - 686 # . . push args - 687 68/push "Ac"/imm32 - 688 51/push-ECX - 689 # . . call - 690 e8/call slice-starts-with?/disp32 - 691 # . . discard args - 692 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 693 # check-ints-equal(EAX, 0, msg) - 694 # . . push args - 695 68/push "F - test-slice-starts-with-fails-2"/imm32 - 696 68/push 0/imm32 - 697 50/push-EAX - 698 # . . call - 699 e8/call check-ints-equal/disp32 - 700 # . . discard args - 701 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 702 # . epilog - 703 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 704 5d/pop-to-EBP - 705 c3/return - 706 - 707 write-slice-buffered: # out : (address buffered-file), s : (address slice) - 708 # . prolog - 709 55/push-EBP - 710 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 711 # . save registers - 712 50/push-EAX - 713 51/push-ECX - 714 52/push-EDX - 715 53/push-EBX - 716 56/push-ESI - 717 57/push-EDI - 718 # ESI = s - 719 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 720 # curr/ECX = s->start - 721 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - 722 # max/ESI = s->end - 723 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI - 724 # EDI = out - 725 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - 726 # EDX = out->length - 727 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 0xc/disp8 . # copy *(EDI+12) to EDX - 728 # EBX = out->write - 729 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy *(EDI+4) to EBX - 730 $write-slice-buffered:loop: - 731 # if (curr >= max) break - 732 39/compare 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # compare ECX with ESI - 733 7d/jump-if-greater-or-equal $write-slice-buffered:loop-end/disp8 - 734 # if (out->write >= out->length) flush and clear out's stream - 735 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX - 736 7c/jump-if-lesser $write-slice-buffered:to-stream/disp8 - 737 # . persist out->write - 738 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) - 739 # . flush(out) - 740 # . . push args - 741 57/push-EDI + 267 68/push "Abc"/imm32 + 268 51/push-ECX + 269 # . . call + 270 e8/call slice-equal?/disp32 + 271 # . . discard args + 272 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 273 # check-ints-equal(EAX, 0, msg) + 274 # . . push args + 275 68/push "F - test-slice-equal-too-long"/imm32 + 276 68/push 0/imm32 + 277 50/push-EAX + 278 # . . call + 279 e8/call check-ints-equal/disp32 + 280 # . . discard args + 281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 282 # . epilog + 283 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 284 5d/pop-to-EBP + 285 c3/return + 286 + 287 test-slice-equal-too-short: + 288 # - slice-equal?(slice("A"), "Abc") == 0 + 289 # . prolog + 290 55/push-EBP + 291 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 292 # (EAX..ECX) = "A" + 293 b8/copy-to-EAX "A"/imm32 + 294 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 295 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 296 05/add-to-EAX 4/imm32 + 297 # var slice/ECX = {EAX, ECX} + 298 51/push-ECX + 299 50/push-EAX + 300 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 301 # EAX = slice-equal?(ECX, "Abc") + 302 # . . push args + 303 68/push "Abc"/imm32 + 304 51/push-ECX + 305 # . . call + 306 e8/call slice-equal?/disp32 + 307 # . . discard args + 308 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 309 # check-ints-equal(EAX, 0, msg) + 310 # . . push args + 311 68/push "F - test-slice-equal-too-short"/imm32 + 312 68/push 0/imm32 + 313 50/push-EAX + 314 # . . call + 315 e8/call check-ints-equal/disp32 + 316 # . . discard args + 317 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 318 # . epilog + 319 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 320 5d/pop-to-EBP + 321 c3/return + 322 + 323 test-slice-equal-empty: + 324 # - slice-equal?(slice(""), "Abc") == 0 + 325 # . prolog + 326 55/push-EBP + 327 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 328 # var slice/ECX + 329 68/push 0/imm32/end + 330 68/push 0/imm32/start + 331 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 332 # EAX = slice-equal?(ECX, "Abc") + 333 # . . push args + 334 68/push "Abc"/imm32 + 335 51/push-ECX + 336 # . . call + 337 e8/call slice-equal?/disp32 + 338 # . . discard args + 339 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 340 # check-ints-equal(EAX, 0, msg) + 341 # . . push args + 342 68/push "F - test-slice-equal-empty"/imm32 + 343 68/push 0/imm32 + 344 50/push-EAX + 345 # . . call + 346 e8/call check-ints-equal/disp32 + 347 # . . discard args + 348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 349 # . epilog + 350 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 351 5d/pop-to-EBP + 352 c3/return + 353 + 354 test-slice-equal-with-empty: + 355 # - slice-equal?(slice("Ab"), "") == 0 + 356 # . prolog + 357 55/push-EBP + 358 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 359 # (EAX..ECX) = "Ab" + 360 b8/copy-to-EAX "Ab"/imm32 + 361 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 362 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 363 05/add-to-EAX 4/imm32 + 364 # var slice/ECX = {EAX, ECX} + 365 51/push-ECX + 366 50/push-EAX + 367 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 368 # EAX = slice-equal?(ECX, "") + 369 # . . push args + 370 68/push ""/imm32 + 371 51/push-ECX + 372 # . . call + 373 e8/call slice-equal?/disp32 + 374 # . . discard args + 375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 376 # check-ints-equal(EAX, 0, msg) + 377 # . . push args + 378 68/push "F - test-slice-equal-with-empty"/imm32 + 379 68/push 0/imm32 + 380 50/push-EAX + 381 # . . call + 382 e8/call check-ints-equal/disp32 + 383 # . . discard args + 384 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 385 # . epilog + 386 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 387 5d/pop-to-EBP + 388 c3/return + 389 + 390 test-slice-equal-empty-with-empty: + 391 # - slice-equal?(slice(""), "") == 1 + 392 # . prolog + 393 55/push-EBP + 394 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 395 # var slice/ECX + 396 68/push 0/imm32/end + 397 68/push 0/imm32/start + 398 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 399 # EAX = slice-equal?(ECX, "") + 400 # . . push args + 401 68/push ""/imm32 + 402 51/push-ECX + 403 # . . call + 404 e8/call slice-equal?/disp32 + 405 # . . discard args + 406 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 407 # check-ints-equal(EAX, 1, msg) + 408 # . . push args + 409 68/push "F - test-slice-equal-empty-with-empty"/imm32 + 410 68/push 1/imm32 + 411 50/push-EAX + 412 # . . call + 413 e8/call check-ints-equal/disp32 + 414 # . . discard args + 415 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 416 # . epilog + 417 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 418 5d/pop-to-EBP + 419 c3/return + 420 + 421 test-slice-equal-with-null: + 422 # - slice-equal?(slice("Ab"), null) == 0 + 423 # . prolog + 424 55/push-EBP + 425 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 426 # (EAX..ECX) = "Ab" + 427 b8/copy-to-EAX "Ab"/imm32 + 428 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 429 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 430 05/add-to-EAX 4/imm32 + 431 # var slice/ECX = {EAX, ECX} + 432 51/push-ECX + 433 50/push-EAX + 434 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 435 # EAX = slice-equal?(ECX, 0) + 436 # . . push args + 437 68/push 0/imm32 + 438 51/push-ECX + 439 # . . call + 440 e8/call slice-equal?/disp32 + 441 # . . discard args + 442 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 443 # check-ints-equal(EAX, 0, msg) + 444 # . . push args + 445 68/push "F - test-slice-equal-with-null"/imm32 + 446 68/push 0/imm32 + 447 50/push-EAX + 448 # . . call + 449 e8/call check-ints-equal/disp32 + 450 # . . discard args + 451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 452 # . epilog + 453 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 454 5d/pop-to-EBP + 455 c3/return + 456 + 457 slice-starts-with?: # s : (address slice), head : (address string) -> EAX : boolean + 458 # pseudocode + 459 # lenh = head->length + 460 # if (lenh > s->end - s->start) return false + 461 # i = 0 + 462 # currs = s->start + 463 # currp = head->data + 464 # while i < lenh + 465 # if (*currs != *currh) return false + 466 # ++i + 467 # ++currs + 468 # ++currh + 469 # return true + 470 # + 471 # registers: + 472 # currs: ESI + 473 # currh: EDI + 474 # *currs: EAX + 475 # *currh: EBX + 476 # i: ECX + 477 # lenh: EDX + 478 # + 479 # . prolog + 480 55/push-EBP + 481 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 482 # . save registers + 483 51/push-ECX + 484 52/push-EDX + 485 53/push-EBX + 486 56/push-ESI + 487 57/push-EDI + 488 # ESI = s + 489 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 490 # ECX = s->end - s->start + 491 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX + 492 2b/subtract 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # subtract *ESI from ECX + 493 # EDI = head + 494 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + 495 # lenh/EDX = head->length + 496 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX + 497 # if (lenh > s->end - s->start) return false + 498 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX + 499 7f/jump-if-greater $slice-starts-with?:false/disp8 + 500 # currs/ESI = s->start + 501 8b/subtract 0/mod/indirect 6/rm32/ESI . . . 6/r32/ESI . . # copy *ESI to ESI + 502 # currh/EDI = head->data + 503 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI + 504 # i/ECX = 0 + 505 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX + 506 # EAX = EBX = 0 + 507 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 508 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX + 509 $slice-starts-with?:loop: + 510 # if (i >= lenh) return true + 511 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 512 7d/jump-if-greater-or-equal $slice-starts-with?:true/disp8 + 513 # AL = *currs + 514 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL + 515 # BL = *currh + 516 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at *EDI to BL + 517 # if (*currs != *currh) return false + 518 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX + 519 75/jump-if-not-equal $slice-starts-with?:false/disp8 + 520 # ++i + 521 41/increment-ECX + 522 # ++currs + 523 46/increment-ESI + 524 # ++currh + 525 47/increment-EDI + 526 eb/jump $slice-starts-with?:loop/disp8 + 527 $slice-starts-with?:true: + 528 b8/copy-to-EAX 1/imm32 + 529 eb/jump $slice-starts-with?:end/disp8 + 530 $slice-starts-with?:false: + 531 b8/copy-to-EAX 0/imm32 + 532 $slice-starts-with?:end: + 533 # . restore registers + 534 5f/pop-to-EDI + 535 5e/pop-to-ESI + 536 5b/pop-to-EBX + 537 5a/pop-to-EDX + 538 59/pop-to-ECX + 539 # . epilog + 540 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 541 5d/pop-to-EBP + 542 c3/return + 543 + 544 test-slice-starts-with-single-character: + 545 # - slice-starts-with?(slice("Abc"), "A") == 1 + 546 # . prolog + 547 55/push-EBP + 548 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 549 # (EAX..ECX) = "Abc" + 550 b8/copy-to-EAX "Abc"/imm32 + 551 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 552 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 553 05/add-to-EAX 4/imm32 + 554 # var slice/ECX = {EAX, ECX} + 555 51/push-ECX + 556 50/push-EAX + 557 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 558 # EAX = slice-starts-with?(ECX, "A") + 559 # . . push args + 560 68/push "A"/imm32 + 561 51/push-ECX + 562 # . . call + 563 e8/call slice-starts-with?/disp32 + 564 # . . discard args + 565 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 566 # check-ints-equal(EAX, 1, msg) + 567 # . . push args + 568 68/push "F - test-slice-starts-with-single-character"/imm32 + 569 68/push 1/imm32 + 570 50/push-EAX + 571 # . . call + 572 e8/call check-ints-equal/disp32 + 573 # . . discard args + 574 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 575 # . epilog + 576 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 577 5d/pop-to-EBP + 578 c3/return + 579 + 580 test-slice-starts-with-empty-string: + 581 # - slice-starts-with?(slice("Abc"), "") == 1 + 582 # . prolog + 583 55/push-EBP + 584 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 585 # (EAX..ECX) = "Abc" + 586 b8/copy-to-EAX "Abc"/imm32 + 587 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 588 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 589 05/add-to-EAX 4/imm32 + 590 # var slice/ECX = {EAX, ECX} + 591 51/push-ECX + 592 50/push-EAX + 593 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 594 # EAX = slice-starts-with?(ECX, "") + 595 # . . push args + 596 68/push ""/imm32 + 597 51/push-ECX + 598 # . . call + 599 e8/call slice-starts-with?/disp32 + 600 # . . discard args + 601 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 602 # check-ints-equal(EAX, 1, msg) + 603 # . . push args + 604 68/push "F - test-slice-starts-with-empty-string"/imm32 + 605 68/push 1/imm32 + 606 50/push-EAX + 607 # . . call + 608 e8/call check-ints-equal/disp32 + 609 # . . discard args + 610 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 611 # . epilog + 612 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 613 5d/pop-to-EBP + 614 c3/return + 615 + 616 test-slice-starts-with-multiple-characters: + 617 # - slice-starts-with?(slice("Abc"), "Ab") == 1 + 618 # . prolog + 619 55/push-EBP + 620 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 621 # (EAX..ECX) = "Abc" + 622 b8/copy-to-EAX "Abc"/imm32 + 623 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 624 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 625 05/add-to-EAX 4/imm32 + 626 # var slice/ECX = {EAX, ECX} + 627 51/push-ECX + 628 50/push-EAX + 629 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 630 # EAX = slice-starts-with?(ECX, "Ab") + 631 # . . push args + 632 68/push "Ab"/imm32 + 633 51/push-ECX + 634 # . . call + 635 e8/call slice-starts-with?/disp32 + 636 # . . discard args + 637 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 638 # check-ints-equal(EAX, 1, msg) + 639 # . . push args + 640 68/push "F - test-slice-starts-with-multiple-characters"/imm32 + 641 68/push 1/imm32 + 642 50/push-EAX + 643 # . . call + 644 e8/call check-ints-equal/disp32 + 645 # . . discard args + 646 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 647 # . epilog + 648 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 649 5d/pop-to-EBP + 650 c3/return + 651 + 652 test-slice-starts-with-entire-string: + 653 # - slice-starts-with?(slice("Abc"), "Abc") == 1 + 654 # . prolog + 655 55/push-EBP + 656 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 657 # (EAX..ECX) = "Abc" + 658 b8/copy-to-EAX "Abc"/imm32 + 659 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 660 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 661 05/add-to-EAX 4/imm32 + 662 # var slice/ECX = {EAX, ECX} + 663 51/push-ECX + 664 50/push-EAX + 665 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 666 # EAX = slice-starts-with?(ECX, "Abc") + 667 # . . push args + 668 68/push "Abc"/imm32 + 669 51/push-ECX + 670 # . . call + 671 e8/call slice-starts-with?/disp32 + 672 # . . discard args + 673 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 674 # check-ints-equal(EAX, 1, msg) + 675 # . . push args + 676 68/push "F - test-slice-starts-with-entire-string"/imm32 + 677 68/push 1/imm32 + 678 50/push-EAX + 679 # . . call + 680 e8/call check-ints-equal/disp32 + 681 # . . discard args + 682 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 683 # . epilog + 684 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 685 5d/pop-to-EBP + 686 c3/return + 687 + 688 test-slice-starts-with-fails: + 689 # - slice-starts-with?(slice("Abc"), "Abd") == 1 + 690 # . prolog + 691 55/push-EBP + 692 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 693 # (EAX..ECX) = "Abc" + 694 b8/copy-to-EAX "Abc"/imm32 + 695 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 696 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 697 05/add-to-EAX 4/imm32 + 698 # var slice/ECX = {EAX, ECX} + 699 51/push-ECX + 700 50/push-EAX + 701 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 702 # EAX = slice-starts-with?(ECX, "Abd") + 703 # . . push args + 704 68/push "Abd"/imm32 + 705 51/push-ECX + 706 # . . call + 707 e8/call slice-starts-with?/disp32 + 708 # . . discard args + 709 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 710 # check-ints-equal(EAX, 0, msg) + 711 # . . push args + 712 68/push "F - test-slice-starts-with-fails"/imm32 + 713 68/push 0/imm32 + 714 50/push-EAX + 715 # . . call + 716 e8/call check-ints-equal/disp32 + 717 # . . discard args + 718 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 719 # . epilog + 720 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 721 5d/pop-to-EBP + 722 c3/return + 723 + 724 test-slice-starts-with-fails-2: + 725 # - slice-starts-with?(slice("Abc"), "Ac") == 1 + 726 # . prolog + 727 55/push-EBP + 728 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 729 # (EAX..ECX) = "Abc" + 730 b8/copy-to-EAX "Abc"/imm32 + 731 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 732 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 733 05/add-to-EAX 4/imm32 + 734 # var slice/ECX = {EAX, ECX} + 735 51/push-ECX + 736 50/push-EAX + 737 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 738 # EAX = slice-starts-with?(ECX, "Ac") + 739 # . . push args + 740 68/push "Ac"/imm32 + 741 51/push-ECX 742 # . . call - 743 e8/call flush/disp32 + 743 e8/call slice-starts-with?/disp32 744 # . . discard args - 745 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 746 # . clear-stream(stream = out+4) + 745 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 746 # check-ints-equal(EAX, 0, msg) 747 # . . push args - 748 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+4 to EAX - 749 50/push-EAX - 750 # . . call - 751 e8/call clear-stream/disp32 - 752 # . . discard args - 753 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 754 # . out->write must now be 0; update its cache at EBX - 755 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 756 $write-slice-buffered:to-stream: - 757 # out->data[out->write] = *in - 758 # . AL = *in - 759 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 760 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 761 # . out->data[out->write] = AL - 762 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+EBX+16) - 763 # ++out->write - 764 43/increment-EBX - 765 # ++in - 766 41/increment-ECX - 767 eb/jump $write-slice-buffered:loop/disp8 - 768 $write-slice-buffered:loop-end: - 769 # persist necessary variables from registers - 770 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) - 771 $write-slice-buffered:end: - 772 # . restore registers - 773 5f/pop-to-EDI - 774 5e/pop-to-ESI - 775 5b/pop-to-EBX - 776 5a/pop-to-EDX - 777 59/pop-to-ECX - 778 58/pop-to-EAX - 779 # . epilog - 780 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 781 5d/pop-to-EBP - 782 c3/return - 783 - 784 test-write-slice-buffered: - 785 # . prolog - 786 55/push-EBP - 787 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 788 # setup - 789 # . clear-stream(_test-stream) - 790 # . . push args - 791 68/push _test-stream/imm32 - 792 # . . call - 793 e8/call clear-stream/disp32 - 794 # . . discard args - 795 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 796 # . clear-stream(_test-buffered-file+4) - 797 # . . push args - 798 b8/copy-to-EAX _test-buffered-file/imm32 - 799 05/add-to-EAX 4/imm32 - 800 50/push-EAX - 801 # . . call - 802 e8/call clear-stream/disp32 - 803 # . . discard args - 804 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 805 # var slice/ECX = "Abc" - 806 68/push _test-slice-data-3/imm32/end - 807 68/push _test-slice-data-0/imm32/start - 808 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 809 # write-slice-buffered(_test-buffered-file, slice) - 810 # . . push args - 811 51/push-ECX - 812 68/push _test-buffered-file/imm32 - 813 # . . call - 814 e8/call write-slice-buffered/disp32 - 815 # . . discard args - 816 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 817 # flush(_test-buffered-file) - 818 # . . push args - 819 68/push _test-buffered-file/imm32 - 820 # . . call - 821 e8/call flush/disp32 - 822 # . . discard args - 823 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 824 # check-stream-equal(_test-stream, "Abc", msg) - 825 # . . push args - 826 68/push "F - test-write-slice-buffered"/imm32 - 827 68/push "Abc"/imm32 - 828 68/push _test-stream/imm32 - 829 # . . call - 830 e8/call check-stream-equal/disp32 - 831 # . . discard args - 832 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 833 # . epilog - 834 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 835 5d/pop-to-EBP - 836 c3/return - 837 - 838 # copy a slice into a new (dynamically allocated) string - 839 slice-to-string: # ad : (address allocation-descriptor), in : (address slice) -> out/EAX : (address array) - 840 # . prolog - 841 55/push-EBP - 842 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 843 # . save registers - 844 51/push-ECX - 845 52/push-EDX - 846 53/push-EBX - 847 56/push-ESI - 848 # ESI = in - 849 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 850 # curr/EDX = in->start - 851 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 852 # max/EBX = in->end - 853 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 3/r32/EBX 4/disp8 . # copy *(ESI+4) to EBX - 854 # size/ECX = max - curr + 4 # total size of output string (including the initial length) - 855 89/copy 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # copy EBX to ECX - 856 29/subtract 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # subtract EDX from ECX - 857 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX - 858 # out/EAX = allocate(ad, size) - 859 # . . push args - 860 51/push-ECX - 861 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 862 # . . call - 863 e8/call allocate/disp32 - 864 # . . discard args - 865 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 866 # if (EAX == 0) abort - 867 3d/compare-EAX-and 0/imm32 - 868 74/jump-if-equal $slice-to-string:abort/disp8 - 869 # *out = size-4 - 870 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - 871 81 5/subop/subtract 0/mod/indirect 0/rm32/EAX . . . . . 4/imm32 # subtract 4 from *EAX - 872 # save out - 873 50/push-EAX - 874 # EAX = _append-4(EAX+4, EAX+size, curr, max) # clobbering ECX - 875 # . . push args - 876 53/push-EBX - 877 52/push-EDX - 878 # . . push EAX+size (clobbering ECX) - 879 01/add 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # add EAX to ECX - 880 51/push-ECX - 881 # . . push EAX+4 (clobbering EAX) - 882 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX + 748 68/push "F - test-slice-starts-with-fails-2"/imm32 + 749 68/push 0/imm32 + 750 50/push-EAX + 751 # . . call + 752 e8/call check-ints-equal/disp32 + 753 # . . discard args + 754 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 755 # . epilog + 756 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 757 5d/pop-to-EBP + 758 c3/return + 759 + 760 # write a slice to a stream + 761 # abort if the stream doesn't have enough space + 762 write-slice: # out : (address stream), s : (address slice) + 763 # . prolog + 764 55/push-EBP + 765 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 766 # . save registers + 767 50/push-EAX + 768 51/push-ECX + 769 52/push-EDX + 770 53/push-EBX + 771 56/push-ESI + 772 57/push-EDI + 773 # ESI = s + 774 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + 775 # curr/ECX = s->start + 776 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX + 777 # max/ESI = s->end + 778 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI + 779 # EDI = out + 780 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + 781 # EDX = out->length + 782 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX + 783 # EBX = out->write + 784 8b/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy *EDI to EBX + 785 $write-slice:loop: + 786 # if (curr >= max) break + 787 39/compare 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # compare ECX with ESI + 788 73/jump-if-greater-or-equal-unsigned $write-slice:loop-end/disp8 + 789 # if (out->write >= out->length) abort + 790 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX + 791 7d/jump-if-greater-or-equal $write-slice:abort/disp8 + 792 # out->data[out->write] = *in + 793 # . AL = *in + 794 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 795 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL + 796 # . out->data[out->write] = AL + 797 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EBX+12) + 798 # ++out->write + 799 43/increment-EBX + 800 # ++in + 801 41/increment-ECX + 802 eb/jump $write-slice:loop/disp8 + 803 $write-slice:loop-end: + 804 # persist out->write + 805 89/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy EBX to *EDI + 806 $write-slice:end: + 807 # . restore registers + 808 5f/pop-to-EDI + 809 5e/pop-to-ESI + 810 5b/pop-to-EBX + 811 5a/pop-to-EDX + 812 59/pop-to-ECX + 813 58/pop-to-EAX + 814 # . epilog + 815 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 816 5d/pop-to-EBP + 817 c3/return + 818 + 819 $write-slice:abort: + 820 # . _write(2/stderr, error) + 821 # . . push args + 822 68/push "write-slice: out of space"/imm32 + 823 68/push 2/imm32/stderr + 824 # . . call + 825 e8/call _write/disp32 + 826 # . . discard args + 827 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 828 # . syscall(exit, 1) + 829 bb/copy-to-EBX 1/imm32 + 830 b8/copy-to-EAX 1/imm32/exit + 831 cd/syscall 0x80/imm8 + 832 # never gets here + 833 + 834 test-write-slice: + 835 # . prolog + 836 55/push-EBP + 837 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 838 # setup + 839 # . clear-stream(_test-stream) + 840 # . . push args + 841 68/push _test-stream/imm32 + 842 # . . call + 843 e8/call clear-stream/disp32 + 844 # . . discard args + 845 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 846 # (EAX..ECX) = "Abc" + 847 b8/copy-to-EAX "Abc"/imm32 + 848 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 849 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 850 05/add-to-EAX 4/imm32 + 851 # var slice/ECX = {EAX, ECX} + 852 51/push-ECX + 853 50/push-EAX + 854 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 855 # write-slice(_test-stream, slice) + 856 # . . push args + 857 51/push-ECX + 858 68/push _test-stream/imm32 + 859 # . . call + 860 e8/call write-slice/disp32 + 861 # . . discard args + 862 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 863 # check-stream-equal(_test-stream, "Abc", msg) + 864 # . . push args + 865 68/push "F - test-write-slice"/imm32 + 866 68/push "Abc"/imm32 + 867 68/push _test-stream/imm32 + 868 # . . call + 869 e8/call check-stream-equal/disp32 + 870 # . . discard args + 871 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 872 # . epilog + 873 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 874 5d/pop-to-EBP + 875 c3/return + 876 + 877 # write a slice to a buffered-file + 878 write-slice-buffered: # out : (address buffered-file), s : (address slice) + 879 # . prolog + 880 55/push-EBP + 881 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 882 # . save registers 883 50/push-EAX - 884 # . . call - 885 e8/call _append-4/disp32 - 886 # . . discard args - 887 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 888 # restore out (assumes _append-4 can't error) - 889 58/pop-to-EAX - 890 $slice-to-string:end: - 891 # . restore registers - 892 5e/pop-to-ESI - 893 5b/pop-to-EBX - 894 5a/pop-to-EDX - 895 59/pop-to-ECX - 896 # . epilog - 897 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 898 5d/pop-to-EBP - 899 c3/return - 900 - 901 $slice-to-string:abort: - 902 # . _write(2/stderr, error) - 903 # . . push args - 904 68/push "slice-to-string: out of space\n"/imm32 - 905 68/push 2/imm32/stderr - 906 # . . call - 907 e8/call _write/disp32 - 908 # . . discard args - 909 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 910 # . syscall(exit, 1) - 911 bb/copy-to-EBX 1/imm32 - 912 b8/copy-to-EAX 1/imm32/exit - 913 cd/syscall 0x80/imm8 - 914 # never gets here - 915 - 916 test-slice-to-string: - 917 # . prolog - 918 55/push-EBP - 919 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 920 # var heap/EDX : (address allocation-descriptor) = {0, 0} - 921 68/push 0/imm32/limit - 922 68/push 0/imm32/curr - 923 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - 924 # heap = new-segment(512) - 925 # . . push args - 926 52/push-EDX - 927 68/push 0x200/imm32 - 928 # . . call - 929 e8/call new-segment/disp32 - 930 # . . discard args - 931 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 932 # var slice/ECX = "Abc" - 933 68/push _test-slice-data-3/imm32/end - 934 68/push _test-slice-data-0/imm32/start - 935 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 936 # EAX = slice-to-string(heap, slice) - 937 # . . push args - 938 51/push-ECX - 939 52/push-EDX - 940 # . . call - 941 e8/call slice-to-string/disp32 - 942 # . . discard args - 943 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 944 +-- 26 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- - 970 # EAX = string-equal?(EAX, "Abc") - 971 # . . push args - 972 68/push "Abc"/imm32 - 973 50/push-EAX - 974 # . . call - 975 e8/call string-equal?/disp32 - 976 # . . discard args - 977 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 978 # check-ints-equal(EAX, 1, msg) - 979 # . . push args - 980 68/push "F - test-slice-to-string"/imm32 - 981 68/push 1/imm32/true - 982 50/push-EAX - 983 # . . call - 984 e8/call check-ints-equal/disp32 - 985 # . . discard args - 986 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 987 # . epilog - 988 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 989 5d/pop-to-EBP - 990 c3/return - 991 - 992 == data - 993 - 994 _test-slice-data-0: - 995 41/A - 996 _test-slice-data-1: - 997 62/b - 998 _test-slice-data-2: - 999 63/c -1000 _test-slice-data-3: -1001 64/d -1002 _test-slice-data-4: -1003 -1004 # . . vim:nowrap:textwidth=0 + 884 51/push-ECX + 885 52/push-EDX + 886 53/push-EBX + 887 56/push-ESI + 888 57/push-EDI + 889 # ESI = s + 890 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + 891 # curr/ECX = s->start + 892 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX + 893 # max/ESI = s->end + 894 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI + 895 # EDI = out + 896 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + 897 # EDX = out->length + 898 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 0xc/disp8 . # copy *(EDI+12) to EDX + 899 # EBX = out->write + 900 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy *(EDI+4) to EBX + 901 $write-slice-buffered:loop: + 902 # if (curr >= max) break + 903 39/compare 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # compare ECX with ESI + 904 73/jump-if-greater-or-equal-unsigned $write-slice-buffered:loop-end/disp8 + 905 # if (out->write >= out->length) flush and clear out's stream + 906 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX + 907 7c/jump-if-lesser $write-slice-buffered:to-stream/disp8 + 908 # . persist out->write + 909 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) + 910 # . flush(out) + 911 # . . push args + 912 57/push-EDI + 913 # . . call + 914 e8/call flush/disp32 + 915 # . . discard args + 916 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 917 # . clear-stream(stream = out+4) + 918 # . . push args + 919 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+4 to EAX + 920 50/push-EAX + 921 # . . call + 922 e8/call clear-stream/disp32 + 923 # . . discard args + 924 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 925 # . out->write must now be 0; update its cache at EBX + 926 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX + 927 $write-slice-buffered:to-stream: + 928 # out->data[out->write] = *in + 929 # . AL = *in + 930 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 931 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL + 932 # . out->data[out->write] = AL + 933 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+EBX+16) + 934 # ++out->write + 935 43/increment-EBX + 936 # ++in + 937 41/increment-ECX + 938 eb/jump $write-slice-buffered:loop/disp8 + 939 $write-slice-buffered:loop-end: + 940 # persist necessary variables from registers + 941 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) + 942 $write-slice-buffered:end: + 943 # . restore registers + 944 5f/pop-to-EDI + 945 5e/pop-to-ESI + 946 5b/pop-to-EBX + 947 5a/pop-to-EDX + 948 59/pop-to-ECX + 949 58/pop-to-EAX + 950 # . epilog + 951 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 952 5d/pop-to-EBP + 953 c3/return + 954 + 955 test-write-slice-buffered: + 956 # . prolog + 957 55/push-EBP + 958 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 959 # setup + 960 # . clear-stream(_test-stream) + 961 # . . push args + 962 68/push _test-stream/imm32 + 963 # . . call + 964 e8/call clear-stream/disp32 + 965 # . . discard args + 966 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 967 # . clear-stream(_test-buffered-file+4) + 968 # . . push args + 969 b8/copy-to-EAX _test-buffered-file/imm32 + 970 05/add-to-EAX 4/imm32 + 971 50/push-EAX + 972 # . . call + 973 e8/call clear-stream/disp32 + 974 # . . discard args + 975 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 976 # (EAX..ECX) = "Abc" + 977 b8/copy-to-EAX "Abc"/imm32 + 978 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 979 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 980 05/add-to-EAX 4/imm32 + 981 # var slice/ECX = {EAX, ECX} + 982 51/push-ECX + 983 50/push-EAX + 984 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 985 # write-slice-buffered(_test-buffered-file, slice) + 986 # . . push args + 987 51/push-ECX + 988 68/push _test-buffered-file/imm32 + 989 # . . call + 990 e8/call write-slice-buffered/disp32 + 991 # . . discard args + 992 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 993 # flush(_test-buffered-file) + 994 # . . push args + 995 68/push _test-buffered-file/imm32 + 996 # . . call + 997 e8/call flush/disp32 + 998 # . . discard args + 999 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1000 # check-stream-equal(_test-stream, "Abc", msg) +1001 # . . push args +1002 68/push "F - test-write-slice-buffered"/imm32 +1003 68/push "Abc"/imm32 +1004 68/push _test-stream/imm32 +1005 # . . call +1006 e8/call check-stream-equal/disp32 +1007 # . . discard args +1008 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1009 # . epilog +1010 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1011 5d/pop-to-EBP +1012 c3/return +1013 +1014 # copy a slice into a new (dynamically allocated) string +1015 slice-to-string: # ad : (address allocation-descriptor), in : (address slice) -> out/EAX : (address array) +1016 # . prolog +1017 55/push-EBP +1018 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1019 # . save registers +1020 51/push-ECX +1021 52/push-EDX +1022 53/push-EBX +1023 56/push-ESI +1024 # ESI = in +1025 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI +1026 # curr/EDX = in->start +1027 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX +1028 # max/EBX = in->end +1029 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 3/r32/EBX 4/disp8 . # copy *(ESI+4) to EBX +1030 # size/ECX = max - curr + 4 # total size of output string (including the initial length) +1031 89/copy 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # copy EBX to ECX +1032 29/subtract 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # subtract EDX from ECX +1033 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX +1034 # out/EAX = allocate(ad, size) +1035 # . . push args +1036 51/push-ECX +1037 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1038 # . . call +1039 e8/call allocate/disp32 +1040 # . . discard args +1041 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1042 # if (EAX == 0) abort +1043 3d/compare-EAX-and 0/imm32 +1044 74/jump-if-equal $slice-to-string:abort/disp8 +1045 # *out = size-4 +1046 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX +1047 81 5/subop/subtract 0/mod/indirect 0/rm32/EAX . . . . . 4/imm32 # subtract 4 from *EAX +1048 # save out +1049 50/push-EAX +1050 # EAX = _append-4(EAX+4, EAX+size, curr, max) # clobbering ECX +1051 # . . push args +1052 53/push-EBX +1053 52/push-EDX +1054 # . . push EAX+size (clobbering ECX) +1055 01/add 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # add EAX to ECX +1056 51/push-ECX +1057 # . . push EAX+4 (clobbering EAX) +1058 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX +1059 50/push-EAX +1060 # . . call +1061 e8/call _append-4/disp32 +1062 # . . discard args +1063 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +1064 # restore out (assumes _append-4 can't error) +1065 58/pop-to-EAX +1066 $slice-to-string:end: +1067 # . restore registers +1068 5e/pop-to-ESI +1069 5b/pop-to-EBX +1070 5a/pop-to-EDX +1071 59/pop-to-ECX +1072 # . epilog +1073 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1074 5d/pop-to-EBP +1075 c3/return +1076 +1077 $slice-to-string:abort: +1078 # . _write(2/stderr, error) +1079 # . . push args +1080 68/push "slice-to-string: out of space\n"/imm32 +1081 68/push 2/imm32/stderr +1082 # . . call +1083 e8/call _write/disp32 +1084 # . . discard args +1085 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1086 # . syscall(exit, 1) +1087 bb/copy-to-EBX 1/imm32 +1088 b8/copy-to-EAX 1/imm32/exit +1089 cd/syscall 0x80/imm8 +1090 # never gets here +1091 +1092 test-slice-to-string: +1093 # . prolog +1094 55/push-EBP +1095 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1096 # var heap/EDX : (address allocation-descriptor) = {0, 0} +1097 68/push 0/imm32/limit +1098 68/push 0/imm32/curr +1099 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX +1100 # heap = new-segment(512) +1101 # . . push args +1102 52/push-EDX +1103 68/push 0x200/imm32 +1104 # . . call +1105 e8/call new-segment/disp32 +1106 # . . discard args +1107 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1108 # (EAX..ECX) = "Abc" +1109 b8/copy-to-EAX "Abc"/imm32 +1110 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1111 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +1112 05/add-to-EAX 4/imm32 +1113 # var slice/ECX = {EAX, ECX} +1114 51/push-ECX +1115 50/push-EAX +1116 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1117 # EAX = slice-to-string(heap, slice) +1118 # . . push args +1119 51/push-ECX +1120 52/push-EDX +1121 # . . call +1122 e8/call slice-to-string/disp32 +1123 # . . discard args +1124 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1125 +-- 26 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- +1151 # EAX = string-equal?(EAX, "Abc") +1152 # . . push args +1153 68/push "Abc"/imm32 +1154 50/push-EAX +1155 # . . call +1156 e8/call string-equal?/disp32 +1157 # . . discard args +1158 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1159 # check-ints-equal(EAX, 1, msg) +1160 # . . push args +1161 68/push "F - test-slice-to-string"/imm32 +1162 68/push 1/imm32/true +1163 50/push-EAX +1164 # . . call +1165 e8/call check-ints-equal/disp32 +1166 # . . discard args +1167 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1168 # . epilog +1169 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1170 5d/pop-to-EBP +1171 c3/return +1172 +1173 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/073next-token.subx.html b/html/subx/073next-token.subx.html index 3e6052b8..d477cd53 100644 --- a/html/subx/073next-token.subx.html +++ b/html/subx/073next-token.subx.html @@ -3,8 +3,8 @@ Mu - subx/073next-token.subx - - + + @@ -15,14 +15,13 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } -.CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } +.LineNr { } .Constant { color: #008787; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.subxTest { color: #5f8700; } +.subxS2Comment { color: #8a8a8a; } --> @@ -39,7 +38,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -58,908 +57,903 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/073next-token.subx
-  1 == code
-  2 #   instruction                     effective address                                                   register    displacement    immediate
-  3 # . op          subop               mod             rm32          base        index         scale       r32
-  4 # . 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
-  5 
-  6 #? Entry:  # run a single test, while debugging
-  7 #? e8/call test-next-token-from-slice/disp32
-  8 #?     # syscall(exit, Num-test-failures)
-  9 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 10 #?     b8/copy-to-EAX  1/imm32/exit
- 11 #?     cd/syscall  0x80/imm8
- 12 
- 13 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
- 14 # on reaching end of file, return an empty interval
- 15 next-token:  # in : (address stream), delimiter : byte, out : (address slice)
- 16     # . prolog
- 17     55/push-EBP
- 18     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 19     # . save registers
- 20     50/push-EAX
- 21     51/push-ECX
- 22     56/push-ESI
- 23     57/push-EDI
- 24     # ESI = in
- 25     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
- 26     # EDI = out
- 27     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x10/disp8      .                 # copy *(EBP+16) to EDI
- 28     # skip-chars-matching(in, delimiter)
- 29     # . . push args
- 30     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 31     56/push-ESI
- 32     # . . call
- 33     e8/call  skip-chars-matching/disp32
- 34     # . . discard args
- 35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 36     # out->start = &in->data[in->read]
- 37     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
- 38     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
- 39     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
- 40     # skip-chars-not-matching(in, delimiter)
- 41     # . . push args
- 42     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 43     56/push-ESI
- 44     # . . call
- 45     e8/call  skip-chars-not-matching/disp32
- 46     # . . discard args
- 47     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 48     # out->end = &in->data[in->read]
- 49     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
- 50     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
- 51     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
- 52     # . restore registers
- 53     5f/pop-to-EDI
- 54     5e/pop-to-ESI
- 55     59/pop-to-ECX
- 56     58/pop-to-EAX
- 57     # . epilog
- 58     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 59     5d/pop-to-EBP
- 60     c3/return
- 61 
- 62 test-next-token:
- 63     # . prolog
- 64     55/push-EBP
- 65     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 66     # setup
- 67     # . clear-stream(_test-stream)
- 68     # . . push args
- 69     68/push  _test-stream/imm32
- 70     # . . call
- 71     e8/call  clear-stream/disp32
- 72     # . . discard args
- 73     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 74     # var slice/ECX = {0, 0}
- 75     68/push  0/imm32/end
- 76     68/push  0/imm32/start
- 77     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 78     # write(_test-stream, "  ab")
- 79     # . . push args
- 80     68/push  "  ab"/imm32
- 81     68/push  _test-stream/imm32
- 82     # . . call
- 83     e8/call  write/disp32
- 84     # . . discard args
- 85     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 86     # next-token(_test-stream, 0x20/space, slice)
- 87     # . . push args
- 88     51/push-ECX
- 89     68/push  0x20/imm32
- 90     68/push  _test-stream/imm32
- 91     # . . call
- 92     e8/call  next-token/disp32
- 93     # . . discard args
- 94     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 95     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
- 96     # . check-ints-equal(slice->start - _test-stream, 14, msg)
- 97     # . . push args
- 98     68/push  "F - test-next-token: start"/imm32
- 99     68/push  0xe/imm32
-100     # . . push slice->start - _test-stream
-101     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-102     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-103     50/push-EAX
-104     # . . call
-105     e8/call  check-ints-equal/disp32
-106     # . . discard args
-107     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-108     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
-109     # . check-ints-equal(slice->end - _test-stream, 16, msg)
-110     # . . push args
-111     68/push  "F - test-next-token: end"/imm32
-112     68/push  0x10/imm32
-113     # . . push slice->end - _test-stream
-114     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-115     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-116     50/push-EAX
-117     # . . call
-118     e8/call  check-ints-equal/disp32
-119     # . . discard args
-120     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-121     # . epilog
-122     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-123     5d/pop-to-EBP
-124     c3/return
-125 
-126 test-next-token-Eof:
-127     # . prolog
-128     55/push-EBP
-129     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-130     # setup
-131     # . clear-stream(_test-stream)
-132     # . . push args
-133     68/push  _test-stream/imm32
-134     # . . call
-135     e8/call  clear-stream/disp32
-136     # . . discard args
-137     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-138     # var slice/ECX = {0, 0}
-139     68/push  0/imm32/end
-140     68/push  0/imm32/start
-141     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-142     # write nothing to _test-stream
-143     # next-token(_test-stream, 0x20/space, slice)
-144     # . . push args
-145     51/push-ECX
-146     68/push  0x20/imm32
-147     68/push  _test-stream/imm32
-148     # . . call
-149     e8/call  next-token/disp32
-150     # . . discard args
-151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-152     # check-ints-equal(slice->end, slice->start, msg)
-153     # . . push args
-154     68/push  "F - test-next-token-Eof"/imm32
-155     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
-156     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
-157     # . . call
-158     e8/call  check-ints-equal/disp32
-159     # . . discard args
-160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-161     # . epilog
-162     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-163     5d/pop-to-EBP
-164     c3/return
-165 
-166 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
-167 # on reaching end of file, return an empty interval
-168 next-token-from-slice:  # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> <void>
-169     # . prolog
-170     55/push-EBP
-171     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-172     # . save registers
-173     50/push-EAX
-174     51/push-ECX
-175     52/push-EDX
-176     57/push-EDI
-177     # ECX = end
-178     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
-179     # EDX = delimiter
-180     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
-181     # EDI = out
-182     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x14/disp8      .                 # copy *(EBP+20) to EDI
-183     # EAX = skip-chars-matching-in-slice(start, end, delimiter)
-184     # . . push args
-185     52/push-EDX
-186     51/push-ECX
-187     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-188     # . . call
-189     e8/call  skip-chars-matching-in-slice/disp32
-190     # . . discard args
-191     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-192     # out->start = EAX
-193     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
-194     # EAX = skip-chars-not-matching-in-slice(EAX, end, delimiter)
-195     # . . push args
-196     52/push-EDX
-197     51/push-ECX
-198     50/push-EAX
-199     # . . call
-200     e8/call  skip-chars-not-matching-in-slice/disp32
-201     # . . discard args
-202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-203     # out->end = EAX
-204     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-205     # . restore registers
-206     5f/pop-to-EDI
-207     5a/pop-to-EDX
-208     59/pop-to-ECX
-209     58/pop-to-EAX
-210     # . epilog
-211     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-212     5d/pop-to-EBP
-213     c3/return
-214 
-215 test-next-token-from-slice:
-216     # . prolog
-217     55/push-EBP
-218     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-219     # (EAX..ECX) = "  ab"
-220     b8/copy-to-EAX  "  ab"/imm32
-221     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-222     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-223     05/add-to-EAX  4/imm32
-224     # var out/EDI : (address slice) = {0, 0}
-225     68/push  0/imm32/end
-226     68/push  0/imm32/start
-227     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-228     # next-token-from-slice(EAX, ECX, 0x20/space, out)
-229     # . . push args
-230     57/push-EDI
-231     68/push  0x20/imm32
-232     51/push-ECX
-233     50/push-EAX
-234     # . . call
-235     e8/call  next-token-from-slice/disp32
-236     # . . discard args
-237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-238     # out->start should be at the 'a'
-239     # . check-ints-equal(out->start - in->start, 2, msg)
-240     # . . push args
-241     68/push  "F - test-next-token-from-slice: start"/imm32
-242     68/push  2/imm32
-243     # . . push out->start - in->start
-244     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
-245     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
-246     51/push-ECX
-247     # . . call
-248     e8/call  check-ints-equal/disp32
-249     # . . discard args
-250     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-251     # out->end should be after the 'b'
-252     # check-ints-equal(out->end - in->start, 4, msg)
-253     # . . push args
-254     68/push  "F - test-next-token-from-slice: end"/imm32
-255     68/push  4/imm32
-256     # . . push out->end - in->start
-257     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-258     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
-259     51/push-ECX
-260     # . . call
-261     e8/call  check-ints-equal/disp32
-262     # . . discard args
-263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-264     # . epilog
-265     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-266     5d/pop-to-EBP
-267     c3/return
-268 
-269 test-next-token-from-slice-Eof:
-270     # . prolog
-271     55/push-EBP
-272     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-273     # var out/EDI : (address slice) = {0, 0}
-274     68/push  0/imm32/end
-275     68/push  0/imm32/start
-276     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-277     # next-token-from-slice(0, 0, 0x20/space, out)
-278     # . . push args
-279     57/push-EDI
-280     68/push  0x20/imm32
-281     68/push  0/imm32
-282     68/push  0/imm32
-283     # . . call
-284     e8/call  next-token-from-slice/disp32
-285     # . . discard args
-286     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-287     # out should be empty
-288     # . check-ints-equal(out->end - out->start, 0, msg)
-289     # . . push args
-290     68/push  "F - test-next-token-from-slice-Eof"/imm32
-291     68/push  0/imm32
-292     # . . push out->start - in->start
-293     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-294     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
-295     51/push-ECX
-296     # . . call
-297     e8/call  check-ints-equal/disp32
-298     # . . discard args
-299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-300     # . epilog
-301     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-302     5d/pop-to-EBP
-303     c3/return
-304 
-305 test-next-token-from-slice-nothing:
-306     # . prolog
-307     55/push-EBP
-308     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-309     # (EAX..ECX) = "    "
-310     b8/copy-to-EAX  "    "/imm32
-311     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-312     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-313     05/add-to-EAX  4/imm32
-314     # var out/EDI : (address slice) = {0, 0}
-315     68/push  0/imm32/end
-316     68/push  0/imm32/start
-317     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-318     # next-token-from-slice(in, 0x20/space, out)
-319     # . . push args
-320     57/push-EDI
-321     68/push  0x20/imm32
-322     51/push-ECX
-323     50/push-EAX
-324     # . . call
-325     e8/call  next-token-from-slice/disp32
-326     # . . discard args
-327     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-328     # out should be empty
-329     # . check-ints-equal(out->end - out->start, 0, msg)
-330     # . . push args
-331     68/push  "F - test-next-token-from-slice-Eof"/imm32
-332     68/push  0/imm32
-333     # . . push out->start - in->start
-334     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-335     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
-336     51/push-ECX
-337     # . . call
-338     e8/call  check-ints-equal/disp32
-339     # . . discard args
-340     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-341     # . epilog
-342     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-343     5d/pop-to-EBP
-344     c3/return
-345 
-346 skip-chars-matching:  # in : (address stream), delimiter : byte
-347     # . prolog
-348     55/push-EBP
-349     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-350     # . save registers
-351     50/push-EAX
-352     51/push-ECX
-353     52/push-EDX
-354     53/push-EBX
-355     56/push-ESI
-356     # ESI = in
-357     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-358     # ECX = in->read
-359     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-360     # EBX = in->write
-361     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
-362     # EDX = delimiter
-363     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
-364 $skip-chars-matching:loop:
-365     # if (in->read >= in->write) break
-366     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
-367     7d/jump-if-greater-or-equal  $skip-chars-matching:end/disp8
-368     # EAX = in->data[in->read]
-369     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-370     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
-371     # if (EAX != delimiter) break
-372     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
-373     75/jump-if-not-equal  $skip-chars-matching:end/disp8
-374     # ++in->read
-375     41/increment-ECX
-376     eb/jump  $skip-chars-matching:loop/disp8
-377 $skip-chars-matching:end:
-378     # persist in->read
-379     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
-380     # . restore registers
-381     5e/pop-to-ESI
-382     5b/pop-to-EBX
-383     5a/pop-to-EDX
-384     59/pop-to-ECX
-385     58/pop-to-EAX
-386     # . epilog
-387     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-388     5d/pop-to-EBP
-389     c3/return
-390 
-391 test-skip-chars-matching:
-392     # setup
-393     # . clear-stream(_test-stream)
-394     # . . push args
-395     68/push  _test-stream/imm32
-396     # . . call
-397     e8/call  clear-stream/disp32
-398     # . . discard args
-399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-400     # write(_test-stream, "  ab")
-401     # . . push args
-402     68/push  "  ab"/imm32
-403     68/push  _test-stream/imm32
-404     # . . call
-405     e8/call  write/disp32
-406     # . . discard args
-407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-408     # skip-chars-matching(_test-stream, 0x20/space)
-409     # . . push args
-410     68/push  0x20/imm32
-411     68/push  _test-stream/imm32
-412     # . . call
-413     e8/call  skip-chars-matching/disp32
-414     # . . discard args
-415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-416     # check-ints-equal(_test-stream->read, 2, msg)
-417     # . . push args
-418     68/push  "F - test-skip-chars-matching"/imm32
-419     68/push  2/imm32
-420     # . . push *_test-stream->read
-421     b8/copy-to-EAX  _test-stream/imm32
-422     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-423     # . . call
-424     e8/call  check-ints-equal/disp32
-425     # . . discard args
-426     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-427     # end
-428     c3/return
-429 
-430 test-skip-chars-matching-none:
-431     # setup
-432     # . clear-stream(_test-stream)
-433     # . . push args
-434     68/push  _test-stream/imm32
-435     # . . call
-436     e8/call  clear-stream/disp32
-437     # . . discard args
-438     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-439     # write(_test-stream, "ab")
-440     # . . push args
-441     68/push  "ab"/imm32
-442     68/push  _test-stream/imm32
-443     # . . call
-444     e8/call  write/disp32
-445     # . . discard args
-446     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-447     # skip-chars-matching(_test-stream, 0x20/space)
-448     # . . push args
-449     68/push  0x20/imm32
-450     68/push  _test-stream/imm32
-451     # . . call
-452     e8/call  skip-chars-matching/disp32
-453     # . . discard args
-454     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-455     # check-ints-equal(_test-stream->read, 0, msg)
-456     # . . push args
-457     68/push  "F - test-skip-chars-matching-none"/imm32
-458     68/push  0/imm32
-459     # . . push *_test-stream->read
-460     b8/copy-to-EAX  _test-stream/imm32
-461     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-462     # . . call
-463     e8/call  check-ints-equal/disp32
-464     # . . discard args
-465     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-466     # end
-467     c3/return
-468 
-469 # minor fork of 'skip-chars-matching'
-470 skip-chars-not-matching:  # in : (address stream), delimiter : byte
-471     # . prolog
-472     55/push-EBP
-473     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-474     # . save registers
-475     50/push-EAX
-476     51/push-ECX
-477     52/push-EDX
-478     53/push-EBX
-479     56/push-ESI
-480     # ESI = in
-481     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-482     # ECX = in->read
-483     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-484     # EBX = in->write
-485     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
-486     # EDX = delimiter
-487     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
-488 $skip-chars-not-matching:loop:
-489     # if (in->read >= in->write) break
-490     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
-491     7d/jump-if-greater-or-equal  $skip-chars-not-matching:end/disp8
-492     # EAX = in->data[in->read]
-493     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-494     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
-495     # if (EAX == delimiter) break
-496     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
-497     74/jump-if-equal  $skip-chars-not-matching:end/disp8
-498     # ++in->read
-499     41/increment-ECX
-500     eb/jump  $skip-chars-not-matching:loop/disp8
-501 $skip-chars-not-matching:end:
-502     # persist in->read
-503     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
-504     # . restore registers
-505     5e/pop-to-ESI
-506     5b/pop-to-EBX
-507     5a/pop-to-EDX
-508     59/pop-to-ECX
-509     58/pop-to-EAX
-510     # . epilog
-511     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-512     5d/pop-to-EBP
-513     c3/return
-514 
-515 test-skip-chars-not-matching:
-516     # setup
-517     # . clear-stream(_test-stream)
-518     # . . push args
-519     68/push  _test-stream/imm32
-520     # . . call
-521     e8/call  clear-stream/disp32
-522     # . . discard args
-523     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-524     # write(_test-stream, "ab ")
-525     # . . push args
-526     68/push  "ab "/imm32
-527     68/push  _test-stream/imm32
-528     # . . call
-529     e8/call  write/disp32
-530     # . . discard args
-531     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-532     # skip-chars-not-matching(_test-stream, 0x20/space)
-533     # . . push args
-534     68/push  0x20/imm32
-535     68/push  _test-stream/imm32
-536     # . . call
-537     e8/call  skip-chars-not-matching/disp32
-538     # . . discard args
-539     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-540     # check-ints-equal(_test-stream->read, 2, msg)
-541     # . . push args
-542     68/push  "F - test-skip-chars-not-matching"/imm32
-543     68/push  2/imm32
-544     # . . push *_test-stream->read
-545     b8/copy-to-EAX  _test-stream/imm32
-546     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-547     # . . call
-548     e8/call  check-ints-equal/disp32
-549     # . . discard args
-550     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-551     # end
-552     c3/return
-553 
-554 test-skip-chars-not-matching-none:
-555     # setup
-556     # . clear-stream(_test-stream)
-557     # . . push args
-558     68/push  _test-stream/imm32
-559     # . . call
-560     e8/call  clear-stream/disp32
-561     # . . discard args
-562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-563     # write(_test-stream, " ab")
-564     # . . push args
-565     68/push  " ab"/imm32
-566     68/push  _test-stream/imm32
-567     # . . call
-568     e8/call  write/disp32
-569     # . . discard args
-570     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-571     # skip-chars-not-matching(_test-stream, 0x20/space)
-572     # . . push args
-573     68/push  0x20/imm32
-574     68/push  _test-stream/imm32
-575     # . . call
-576     e8/call  skip-chars-not-matching/disp32
-577     # . . discard args
-578     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-579     # check-ints-equal(_test-stream->read, 0, msg)
-580     # . . push args
-581     68/push  "F - test-skip-chars-not-matching-none"/imm32
-582     68/push  0/imm32
-583     # . . push *_test-stream->read
-584     b8/copy-to-EAX  _test-stream/imm32
-585     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-586     # . . call
-587     e8/call  check-ints-equal/disp32
-588     # . . discard args
-589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-590     # end
-591     c3/return
-592 
-593 test-skip-chars-not-matching-all:
-594     # setup
-595     # . clear-stream(_test-stream)
-596     # . . push args
-597     68/push  _test-stream/imm32
-598     # . . call
-599     e8/call  clear-stream/disp32
-600     # . . discard args
-601     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-602     # write(_test-stream, "ab")
-603     # . . push args
-604     68/push  "ab"/imm32
-605     68/push  _test-stream/imm32
-606     # . . call
-607     e8/call  write/disp32
-608     # . . discard args
-609     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-610     # skip-chars-not-matching(_test-stream, 0x20/space)
-611     # . . push args
-612     68/push  0x20/imm32
-613     68/push  _test-stream/imm32
-614     # . . call
-615     e8/call  skip-chars-not-matching/disp32
-616     # . . discard args
-617     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-618     # check-ints-equal(_test-stream->read, 2, msg)
-619     # . . push args
-620     68/push  "F - test-skip-chars-not-matching-all"/imm32
-621     68/push  2/imm32
-622     # . . push *_test-stream->read
-623     b8/copy-to-EAX  _test-stream/imm32
-624     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-625     # . . call
-626     e8/call  check-ints-equal/disp32
-627     # . . discard args
-628     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-629     # end
-630     c3/return
-631 
-632 skip-chars-not-matching-whitespace:  # in : (address stream)
-633     # . prolog
-634     55/push-EBP
-635     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-636     # . save registers
-637     50/push-EAX
-638     51/push-ECX
-639     53/push-EBX
-640     56/push-ESI
-641     # ESI = in
-642     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-643     # ECX = in->read
-644     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-645     # EBX = in->write
-646     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
-647 $skip-chars-not-matching-whitespace:loop:
-648     # if (in->read >= in->write) break
-649     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
-650     7d/jump-if-greater-or-equal  $skip-chars-not-matching-whitespace:end/disp8
-651     # EAX = in->data[in->read]
-652     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-653     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
-654     # if (EAX == ' ') break
-655     3d/compare-EAX-and  0x20/imm32/space
-656     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
-657     # if (EAX == '\n') break
-658     3d/compare-EAX-and  0x0a/imm32/newline
-659     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
-660     # if (EAX == '\t') break
-661     3d/compare-EAX-and  0x09/imm32/tab
-662     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
-663     # if (EAX == '\r') break
-664     3d/compare-EAX-and  0x0d/imm32/cr
-665     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
-666     # ++in->read
-667     41/increment-ECX
-668     eb/jump  $skip-chars-not-matching-whitespace:loop/disp8
-669 $skip-chars-not-matching-whitespace:end:
-670     # persist in->read
-671     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
-672     # . restore registers
-673     5e/pop-to-ESI
-674     5b/pop-to-EBX
-675     59/pop-to-ECX
-676     58/pop-to-EAX
-677     # . epilog
-678     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-679     5d/pop-to-EBP
-680     c3/return
-681 
-682 skip-chars-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
-683     # . prolog
-684     55/push-EBP
-685     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-686     # . save registers
-687     51/push-ECX
-688     52/push-EDX
-689     53/push-EBX
-690     # EAX = curr
-691     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-692     # ECX = end
-693     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
-694     # EDX = delimiter
-695     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
-696     # EBX = 0
-697     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-698 $skip-chars-matching-in-slice:loop:
-699     # if (curr >= end) break
-700     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
-701     7d/jump-if-greater-or-equal  $skip-chars-matching-in-slice:end/disp8
-702     # if (*curr != delimiter) break
-703     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
-704     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
-705     75/jump-if-not-equal  $skip-chars-matching-in-slice:end/disp8
-706     # ++curr
-707     40/increment-EAX
-708     eb/jump  $skip-chars-matching-in-slice:loop/disp8
-709 $skip-chars-matching-in-slice:end:
-710     # . restore registers
-711     5b/pop-to-EBX
-712     5a/pop-to-EDX
-713     59/pop-to-ECX
-714     # . epilog
-715     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-716     5d/pop-to-EBP
-717     c3/return
-718 
-719 test-skip-chars-matching-in-slice:
-720     # (EAX..ECX) = "  ab"
-721     b8/copy-to-EAX  "  ab"/imm32
-722     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-723     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-724     05/add-to-EAX  4/imm32
-725     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
-726     # . . push args
-727     68/push  0x20/imm32
-728     51/push-ECX
-729     50/push-EAX
-730     # . . call
-731     e8/call  skip-chars-matching-in-slice/disp32
-732     # . . discard args
-733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-734     # check-ints-equal(ECX-EAX, 2, msg)
-735     # . . push args
-736     68/push  "F - test-skip-chars-matching-in-slice"/imm32
-737     68/push  2/imm32
-738     # . . push ECX-EAX
-739     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-740     51/push-ECX
-741     # . . call
-742     e8/call  check-ints-equal/disp32
-743     # . . discard args
-744     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-745     # end
-746     c3/return
-747 
-748 test-skip-chars-matching-in-slice-none:
-749     # (EAX..ECX) = "ab"
-750     b8/copy-to-EAX  "ab"/imm32
-751     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-752     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-753     05/add-to-EAX  4/imm32
-754     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
-755     # . . push args
-756     68/push  0x20/imm32
-757     51/push-ECX
-758     50/push-EAX
-759     # . . call
-760     e8/call  skip-chars-matching-in-slice/disp32
-761     # . . discard args
-762     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-763     # check-ints-equal(ECX-EAX, 2, msg)
-764     # . . push args
-765     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
-766     68/push  2/imm32
-767     # . . push ECX-EAX
-768     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-769     51/push-ECX
-770     # . . call
-771     e8/call  check-ints-equal/disp32
-772     # . . discard args
-773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-774     # end
-775     c3/return
-776 
-777 # minor fork of 'skip-chars-matching-in-slice'
-778 skip-chars-not-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
-779     # . prolog
-780     55/push-EBP
-781     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-782     # . save registers
-783     51/push-ECX
-784     52/push-EDX
-785     53/push-EBX
-786     # EAX = curr
-787     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-788     # ECX = end
-789     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
-790     # EDX = delimiter
-791     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
-792     # EBX = 0
-793     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-794 $skip-chars-not-matching-in-slice:loop:
-795     # if (curr >= end) break
-796     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
-797     7d/jump-if-greater-or-equal  $skip-chars-not-matching-in-slice:end/disp8
-798     # if (*curr == delimiter) break
-799     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
-800     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
-801     74/jump-if-equal  $skip-chars-not-matching-in-slice:end/disp8
-802     # ++curr
-803     40/increment-EAX
-804     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
-805 $skip-chars-not-matching-in-slice:end:
-806     # . restore registers
-807     5b/pop-to-EBX
-808     5a/pop-to-EDX
-809     59/pop-to-ECX
-810     # . epilog
-811     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-812     5d/pop-to-EBP
-813     c3/return
-814 
-815 test-skip-chars-not-matching-in-slice:
-816     # (EAX..ECX) = "ab "
-817     b8/copy-to-EAX  "ab "/imm32
-818     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-819     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-820     05/add-to-EAX  4/imm32
-821     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
-822     # . . push args
-823     68/push  0x20/imm32
-824     51/push-ECX
-825     50/push-EAX
-826     # . . call
-827     e8/call  skip-chars-not-matching-in-slice/disp32
-828     # . . discard args
-829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-830     # check-ints-equal(ECX-EAX, 1, msg)
-831     # . . push args
-832     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
-833     68/push  1/imm32
-834     # . . push ECX-EAX
-835     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-836     51/push-ECX
-837     # . . call
-838     e8/call  check-ints-equal/disp32
-839     # . . discard args
-840     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-841     # end
-842     c3/return
-843 
-844 test-skip-chars-not-matching-in-slice-none:
-845     # (EAX..ECX) = " ab"
-846     b8/copy-to-EAX  " ab"/imm32
-847     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-848     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-849     05/add-to-EAX  4/imm32
-850     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
-851     # . . push args
-852     68/push  0x20/imm32
-853     51/push-ECX
-854     50/push-EAX
-855     # . . call
-856     e8/call  skip-chars-not-matching-in-slice/disp32
-857     # . . discard args
-858     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-859     # check-ints-equal(ECX-EAX, 3, msg)
-860     # . . push args
-861     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
-862     68/push  3/imm32
-863     # . . push ECX-EAX
-864     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-865     51/push-ECX
-866     # . . call
-867     e8/call  check-ints-equal/disp32
-868     # . . discard args
-869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-870     # end
-871     c3/return
-872 
-873 test-skip-chars-not-matching-in-slice-all:
-874     # (EAX..ECX) = "ab"
-875     b8/copy-to-EAX  "ab"/imm32
-876     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-877     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
-878     05/add-to-EAX  4/imm32
-879     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
-880     # . . push args
-881     68/push  0x20/imm32
-882     51/push-ECX
-883     50/push-EAX
-884     # . . call
-885     e8/call  skip-chars-not-matching-in-slice/disp32
-886     # . . discard args
-887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-888     # check-ints-equal(ECX-EAX, 0, msg)
-889     # . . push args
-890     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
-891     68/push  0/imm32
-892     # . . push ECX-EAX
-893     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-894     51/push-ECX
-895     # . . call
-896     e8/call  check-ints-equal/disp32
-897     # . . discard args
-898     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-899     # end
-900     c3/return
-901 
-902 # . . vim:nowrap:textwidth=0
+  1 # Some tokenization primitives.
+  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 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
+  9 # on reaching end of file, return an empty interval
+ 10 next-token:  # in : (address stream), delimiter : byte, out : (address slice)
+ 11     # . prolog
+ 12     55/push-EBP
+ 13     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 14     # . save registers
+ 15     50/push-EAX
+ 16     51/push-ECX
+ 17     56/push-ESI
+ 18     57/push-EDI
+ 19     # ESI = in
+ 20     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 21     # EDI = out
+ 22     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x10/disp8      .                 # copy *(EBP+16) to EDI
+ 23     # skip-chars-matching(in, delimiter)
+ 24     # . . push args
+ 25     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 26     56/push-ESI
+ 27     # . . call
+ 28     e8/call  skip-chars-matching/disp32
+ 29     # . . discard args
+ 30     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 31     # out->start = &in->data[in->read]
+ 32     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 33     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+ 34     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+ 35     # skip-chars-not-matching(in, delimiter)
+ 36     # . . push args
+ 37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 38     56/push-ESI
+ 39     # . . call
+ 40     e8/call  skip-chars-not-matching/disp32
+ 41     # . . discard args
+ 42     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 43     # out->end = &in->data[in->read]
+ 44     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 45     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+ 46     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+ 47     # . restore registers
+ 48     5f/pop-to-EDI
+ 49     5e/pop-to-ESI
+ 50     59/pop-to-ECX
+ 51     58/pop-to-EAX
+ 52     # . epilog
+ 53     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 54     5d/pop-to-EBP
+ 55     c3/return
+ 56 
+ 57 test-next-token:
+ 58     # . prolog
+ 59     55/push-EBP
+ 60     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 61     # setup
+ 62     # . clear-stream(_test-stream)
+ 63     # . . push args
+ 64     68/push  _test-stream/imm32
+ 65     # . . call
+ 66     e8/call  clear-stream/disp32
+ 67     # . . discard args
+ 68     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 69     # var slice/ECX = {0, 0}
+ 70     68/push  0/imm32/end
+ 71     68/push  0/imm32/start
+ 72     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 73     # write(_test-stream, "  ab")
+ 74     # . . push args
+ 75     68/push  "  ab"/imm32
+ 76     68/push  _test-stream/imm32
+ 77     # . . call
+ 78     e8/call  write/disp32
+ 79     # . . discard args
+ 80     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 81     # next-token(_test-stream, 0x20/space, slice)
+ 82     # . . push args
+ 83     51/push-ECX
+ 84     68/push  0x20/imm32
+ 85     68/push  _test-stream/imm32
+ 86     # . . call
+ 87     e8/call  next-token/disp32
+ 88     # . . discard args
+ 89     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 90     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+ 91     # . check-ints-equal(slice->start - _test-stream, 14, msg)
+ 92     # . . push args
+ 93     68/push  "F - test-next-token: start"/imm32
+ 94     68/push  0xe/imm32
+ 95     # . . push slice->start - _test-stream
+ 96     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+ 97     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+ 98     50/push-EAX
+ 99     # . . call
+100     e8/call  check-ints-equal/disp32
+101     # . . discard args
+102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+103     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
+104     # . check-ints-equal(slice->end - _test-stream, 16, msg)
+105     # . . push args
+106     68/push  "F - test-next-token: end"/imm32
+107     68/push  0x10/imm32
+108     # . . push slice->end - _test-stream
+109     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+110     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+111     50/push-EAX
+112     # . . call
+113     e8/call  check-ints-equal/disp32
+114     # . . discard args
+115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+116     # . epilog
+117     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+118     5d/pop-to-EBP
+119     c3/return
+120 
+121 test-next-token-Eof:
+122     # . prolog
+123     55/push-EBP
+124     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+125     # setup
+126     # . clear-stream(_test-stream)
+127     # . . push args
+128     68/push  _test-stream/imm32
+129     # . . call
+130     e8/call  clear-stream/disp32
+131     # . . discard args
+132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+133     # var slice/ECX = {0, 0}
+134     68/push  0/imm32/end
+135     68/push  0/imm32/start
+136     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+137     # write nothing to _test-stream
+138     # next-token(_test-stream, 0x20/space, slice)
+139     # . . push args
+140     51/push-ECX
+141     68/push  0x20/imm32
+142     68/push  _test-stream/imm32
+143     # . . call
+144     e8/call  next-token/disp32
+145     # . . discard args
+146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+147     # check-ints-equal(slice->end, slice->start, msg)
+148     # . . push args
+149     68/push  "F - test-next-token-Eof"/imm32
+150     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+151     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+152     # . . call
+153     e8/call  check-ints-equal/disp32
+154     # . . discard args
+155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+156     # . epilog
+157     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+158     5d/pop-to-EBP
+159     c3/return
+160 
+161 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
+162 # on reaching end of file, return an empty interval
+163 next-token-from-slice:  # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> <void>
+164     # . prolog
+165     55/push-EBP
+166     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+167     # . save registers
+168     50/push-EAX
+169     51/push-ECX
+170     52/push-EDX
+171     57/push-EDI
+172     # ECX = end
+173     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
+174     # EDX = delimiter
+175     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
+176     # EDI = out
+177     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x14/disp8      .                 # copy *(EBP+20) to EDI
+178     # EAX = skip-chars-matching-in-slice(start, end, delimiter)
+179     # . . push args
+180     52/push-EDX
+181     51/push-ECX
+182     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+183     # . . call
+184     e8/call  skip-chars-matching-in-slice/disp32
+185     # . . discard args
+186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+187     # out->start = EAX
+188     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+189     # EAX = skip-chars-not-matching-in-slice(EAX, end, delimiter)
+190     # . . push args
+191     52/push-EDX
+192     51/push-ECX
+193     50/push-EAX
+194     # . . call
+195     e8/call  skip-chars-not-matching-in-slice/disp32
+196     # . . discard args
+197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+198     # out->end = EAX
+199     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+200     # . restore registers
+201     5f/pop-to-EDI
+202     5a/pop-to-EDX
+203     59/pop-to-ECX
+204     58/pop-to-EAX
+205     # . epilog
+206     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+207     5d/pop-to-EBP
+208     c3/return
+209 
+210 test-next-token-from-slice:
+211     # . prolog
+212     55/push-EBP
+213     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+214     # (EAX..ECX) = "  ab"
+215     b8/copy-to-EAX  "  ab"/imm32
+216     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+217     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+218     05/add-to-EAX  4/imm32
+219     # var out/EDI : (address slice) = {0, 0}
+220     68/push  0/imm32/end
+221     68/push  0/imm32/start
+222     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+223     # next-token-from-slice(EAX, ECX, 0x20/space, out)
+224     # . . push args
+225     57/push-EDI
+226     68/push  0x20/imm32
+227     51/push-ECX
+228     50/push-EAX
+229     # . . call
+230     e8/call  next-token-from-slice/disp32
+231     # . . discard args
+232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+233     # out->start should be at the 'a'
+234     # . check-ints-equal(out->start - in->start, 2, msg)
+235     # . . push args
+236     68/push  "F - test-next-token-from-slice: start"/imm32
+237     68/push  2/imm32
+238     # . . push out->start - in->start
+239     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+240     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
+241     51/push-ECX
+242     # . . call
+243     e8/call  check-ints-equal/disp32
+244     # . . discard args
+245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+246     # out->end should be after the 'b'
+247     # check-ints-equal(out->end - in->start, 4, msg)
+248     # . . push args
+249     68/push  "F - test-next-token-from-slice: end"/imm32
+250     68/push  4/imm32
+251     # . . push out->end - in->start
+252     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+253     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
+254     51/push-ECX
+255     # . . call
+256     e8/call  check-ints-equal/disp32
+257     # . . discard args
+258     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+259     # . epilog
+260     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+261     5d/pop-to-EBP
+262     c3/return
+263 
+264 test-next-token-from-slice-Eof:
+265     # . prolog
+266     55/push-EBP
+267     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+268     # var out/EDI : (address slice) = {0, 0}
+269     68/push  0/imm32/end
+270     68/push  0/imm32/start
+271     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+272     # next-token-from-slice(0, 0, 0x20/space, out)
+273     # . . push args
+274     57/push-EDI
+275     68/push  0x20/imm32
+276     68/push  0/imm32
+277     68/push  0/imm32
+278     # . . call
+279     e8/call  next-token-from-slice/disp32
+280     # . . discard args
+281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+282     # out should be empty
+283     # . check-ints-equal(out->end - out->start, 0, msg)
+284     # . . push args
+285     68/push  "F - test-next-token-from-slice-Eof"/imm32
+286     68/push  0/imm32
+287     # . . push out->start - in->start
+288     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+289     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
+290     51/push-ECX
+291     # . . call
+292     e8/call  check-ints-equal/disp32
+293     # . . discard args
+294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+295     # . epilog
+296     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+297     5d/pop-to-EBP
+298     c3/return
+299 
+300 test-next-token-from-slice-nothing:
+301     # . prolog
+302     55/push-EBP
+303     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+304     # (EAX..ECX) = "    "
+305     b8/copy-to-EAX  "    "/imm32
+306     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+307     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+308     05/add-to-EAX  4/imm32
+309     # var out/EDI : (address slice) = {0, 0}
+310     68/push  0/imm32/end
+311     68/push  0/imm32/start
+312     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+313     # next-token-from-slice(in, 0x20/space, out)
+314     # . . push args
+315     57/push-EDI
+316     68/push  0x20/imm32
+317     51/push-ECX
+318     50/push-EAX
+319     # . . call
+320     e8/call  next-token-from-slice/disp32
+321     # . . discard args
+322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+323     # out should be empty
+324     # . check-ints-equal(out->end - out->start, 0, msg)
+325     # . . push args
+326     68/push  "F - test-next-token-from-slice-Eof"/imm32
+327     68/push  0/imm32
+328     # . . push out->start - in->start
+329     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+330     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
+331     51/push-ECX
+332     # . . call
+333     e8/call  check-ints-equal/disp32
+334     # . . discard args
+335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+336     # . epilog
+337     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+338     5d/pop-to-EBP
+339     c3/return
+340 
+341 skip-chars-matching:  # in : (address stream), delimiter : byte
+342     # . prolog
+343     55/push-EBP
+344     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+345     # . save registers
+346     50/push-EAX
+347     51/push-ECX
+348     52/push-EDX
+349     53/push-EBX
+350     56/push-ESI
+351     # ESI = in
+352     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+353     # ECX = in->read
+354     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+355     # EBX = in->write
+356     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
+357     # EDX = delimiter
+358     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+359 $skip-chars-matching:loop:
+360     # if (in->read >= in->write) break
+361     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
+362     7d/jump-if-greater-or-equal  $skip-chars-matching:end/disp8
+363     # EAX = in->data[in->read]
+364     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+365     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
+366     # if (EAX != delimiter) break
+367     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
+368     75/jump-if-not-equal  $skip-chars-matching:end/disp8
+369     # ++in->read
+370     41/increment-ECX
+371     eb/jump  $skip-chars-matching:loop/disp8
+372 $skip-chars-matching:end:
+373     # persist in->read
+374     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
+375     # . restore registers
+376     5e/pop-to-ESI
+377     5b/pop-to-EBX
+378     5a/pop-to-EDX
+379     59/pop-to-ECX
+380     58/pop-to-EAX
+381     # . epilog
+382     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+383     5d/pop-to-EBP
+384     c3/return
+385 
+386 test-skip-chars-matching:
+387     # setup
+388     # . clear-stream(_test-stream)
+389     # . . push args
+390     68/push  _test-stream/imm32
+391     # . . call
+392     e8/call  clear-stream/disp32
+393     # . . discard args
+394     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+395     # write(_test-stream, "  ab")
+396     # . . push args
+397     68/push  "  ab"/imm32
+398     68/push  _test-stream/imm32
+399     # . . call
+400     e8/call  write/disp32
+401     # . . discard args
+402     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+403     # skip-chars-matching(_test-stream, 0x20/space)
+404     # . . push args
+405     68/push  0x20/imm32
+406     68/push  _test-stream/imm32
+407     # . . call
+408     e8/call  skip-chars-matching/disp32
+409     # . . discard args
+410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+411     # check-ints-equal(_test-stream->read, 2, msg)
+412     # . . push args
+413     68/push  "F - test-skip-chars-matching"/imm32
+414     68/push  2/imm32
+415     # . . push *_test-stream->read
+416     b8/copy-to-EAX  _test-stream/imm32
+417     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+418     # . . call
+419     e8/call  check-ints-equal/disp32
+420     # . . discard args
+421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+422     # end
+423     c3/return
+424 
+425 test-skip-chars-matching-none:
+426     # setup
+427     # . clear-stream(_test-stream)
+428     # . . push args
+429     68/push  _test-stream/imm32
+430     # . . call
+431     e8/call  clear-stream/disp32
+432     # . . discard args
+433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+434     # write(_test-stream, "ab")
+435     # . . push args
+436     68/push  "ab"/imm32
+437     68/push  _test-stream/imm32
+438     # . . call
+439     e8/call  write/disp32
+440     # . . discard args
+441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+442     # skip-chars-matching(_test-stream, 0x20/space)
+443     # . . push args
+444     68/push  0x20/imm32
+445     68/push  _test-stream/imm32
+446     # . . call
+447     e8/call  skip-chars-matching/disp32
+448     # . . discard args
+449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+450     # check-ints-equal(_test-stream->read, 0, msg)
+451     # . . push args
+452     68/push  "F - test-skip-chars-matching-none"/imm32
+453     68/push  0/imm32
+454     # . . push *_test-stream->read
+455     b8/copy-to-EAX  _test-stream/imm32
+456     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+457     # . . call
+458     e8/call  check-ints-equal/disp32
+459     # . . discard args
+460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+461     # end
+462     c3/return
+463 
+464 # minor fork of 'skip-chars-matching'
+465 skip-chars-not-matching:  # in : (address stream), delimiter : byte
+466     # . prolog
+467     55/push-EBP
+468     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+469     # . save registers
+470     50/push-EAX
+471     51/push-ECX
+472     52/push-EDX
+473     53/push-EBX
+474     56/push-ESI
+475     # ESI = in
+476     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+477     # ECX = in->read
+478     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+479     # EBX = in->write
+480     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
+481     # EDX = delimiter
+482     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+483 $skip-chars-not-matching:loop:
+484     # if (in->read >= in->write) break
+485     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
+486     7d/jump-if-greater-or-equal  $skip-chars-not-matching:end/disp8
+487     # EAX = in->data[in->read]
+488     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+489     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
+490     # if (EAX == delimiter) break
+491     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
+492     74/jump-if-equal  $skip-chars-not-matching:end/disp8
+493     # ++in->read
+494     41/increment-ECX
+495     eb/jump  $skip-chars-not-matching:loop/disp8
+496 $skip-chars-not-matching:end:
+497     # persist in->read
+498     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
+499     # . restore registers
+500     5e/pop-to-ESI
+501     5b/pop-to-EBX
+502     5a/pop-to-EDX
+503     59/pop-to-ECX
+504     58/pop-to-EAX
+505     # . epilog
+506     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+507     5d/pop-to-EBP
+508     c3/return
+509 
+510 test-skip-chars-not-matching:
+511     # setup
+512     # . clear-stream(_test-stream)
+513     # . . push args
+514     68/push  _test-stream/imm32
+515     # . . call
+516     e8/call  clear-stream/disp32
+517     # . . discard args
+518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+519     # write(_test-stream, "ab ")
+520     # . . push args
+521     68/push  "ab "/imm32
+522     68/push  _test-stream/imm32
+523     # . . call
+524     e8/call  write/disp32
+525     # . . discard args
+526     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+527     # skip-chars-not-matching(_test-stream, 0x20/space)
+528     # . . push args
+529     68/push  0x20/imm32
+530     68/push  _test-stream/imm32
+531     # . . call
+532     e8/call  skip-chars-not-matching/disp32
+533     # . . discard args
+534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+535     # check-ints-equal(_test-stream->read, 2, msg)
+536     # . . push args
+537     68/push  "F - test-skip-chars-not-matching"/imm32
+538     68/push  2/imm32
+539     # . . push *_test-stream->read
+540     b8/copy-to-EAX  _test-stream/imm32
+541     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+542     # . . call
+543     e8/call  check-ints-equal/disp32
+544     # . . discard args
+545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+546     # end
+547     c3/return
+548 
+549 test-skip-chars-not-matching-none:
+550     # setup
+551     # . clear-stream(_test-stream)
+552     # . . push args
+553     68/push  _test-stream/imm32
+554     # . . call
+555     e8/call  clear-stream/disp32
+556     # . . discard args
+557     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+558     # write(_test-stream, " ab")
+559     # . . push args
+560     68/push  " ab"/imm32
+561     68/push  _test-stream/imm32
+562     # . . call
+563     e8/call  write/disp32
+564     # . . discard args
+565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+566     # skip-chars-not-matching(_test-stream, 0x20/space)
+567     # . . push args
+568     68/push  0x20/imm32
+569     68/push  _test-stream/imm32
+570     # . . call
+571     e8/call  skip-chars-not-matching/disp32
+572     # . . discard args
+573     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+574     # check-ints-equal(_test-stream->read, 0, msg)
+575     # . . push args
+576     68/push  "F - test-skip-chars-not-matching-none"/imm32
+577     68/push  0/imm32
+578     # . . push *_test-stream->read
+579     b8/copy-to-EAX  _test-stream/imm32
+580     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+581     # . . call
+582     e8/call  check-ints-equal/disp32
+583     # . . discard args
+584     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+585     # end
+586     c3/return
+587 
+588 test-skip-chars-not-matching-all:
+589     # setup
+590     # . clear-stream(_test-stream)
+591     # . . push args
+592     68/push  _test-stream/imm32
+593     # . . call
+594     e8/call  clear-stream/disp32
+595     # . . discard args
+596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+597     # write(_test-stream, "ab")
+598     # . . push args
+599     68/push  "ab"/imm32
+600     68/push  _test-stream/imm32
+601     # . . call
+602     e8/call  write/disp32
+603     # . . discard args
+604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+605     # skip-chars-not-matching(_test-stream, 0x20/space)
+606     # . . push args
+607     68/push  0x20/imm32
+608     68/push  _test-stream/imm32
+609     # . . call
+610     e8/call  skip-chars-not-matching/disp32
+611     # . . discard args
+612     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+613     # check-ints-equal(_test-stream->read, 2, msg)
+614     # . . push args
+615     68/push  "F - test-skip-chars-not-matching-all"/imm32
+616     68/push  2/imm32
+617     # . . push *_test-stream->read
+618     b8/copy-to-EAX  _test-stream/imm32
+619     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+620     # . . call
+621     e8/call  check-ints-equal/disp32
+622     # . . discard args
+623     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+624     # end
+625     c3/return
+626 
+627 skip-chars-not-matching-whitespace:  # in : (address stream)
+628     # . prolog
+629     55/push-EBP
+630     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+631     # . save registers
+632     50/push-EAX
+633     51/push-ECX
+634     53/push-EBX
+635     56/push-ESI
+636     # ESI = in
+637     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+638     # ECX = in->read
+639     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+640     # EBX = in->write
+641     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
+642 $skip-chars-not-matching-whitespace:loop:
+643     # if (in->read >= in->write) break
+644     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
+645     7d/jump-if-greater-or-equal  $skip-chars-not-matching-whitespace:end/disp8
+646     # EAX = in->data[in->read]
+647     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+648     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
+649     # if (EAX == ' ') break
+650     3d/compare-EAX-and  0x20/imm32/space
+651     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
+652     # if (EAX == '\n') break
+653     3d/compare-EAX-and  0x0a/imm32/newline
+654     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
+655     # if (EAX == '\t') break
+656     3d/compare-EAX-and  0x09/imm32/tab
+657     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
+658     # if (EAX == '\r') break
+659     3d/compare-EAX-and  0x0d/imm32/cr
+660     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
+661     # ++in->read
+662     41/increment-ECX
+663     eb/jump  $skip-chars-not-matching-whitespace:loop/disp8
+664 $skip-chars-not-matching-whitespace:end:
+665     # persist in->read
+666     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
+667     # . restore registers
+668     5e/pop-to-ESI
+669     5b/pop-to-EBX
+670     59/pop-to-ECX
+671     58/pop-to-EAX
+672     # . epilog
+673     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+674     5d/pop-to-EBP
+675     c3/return
+676 
+677 skip-chars-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
+678     # . prolog
+679     55/push-EBP
+680     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+681     # . save registers
+682     51/push-ECX
+683     52/push-EDX
+684     53/push-EBX
+685     # EAX = curr
+686     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+687     # ECX = end
+688     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
+689     # EDX = delimiter
+690     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
+691     # EBX = 0
+692     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+693 $skip-chars-matching-in-slice:loop:
+694     # if (curr >= end) break
+695     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+696     73/jump-if-greater-or-equal-unsigned  $skip-chars-matching-in-slice:end/disp8
+697     # if (*curr != delimiter) break
+698     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
+699     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
+700     75/jump-if-not-equal  $skip-chars-matching-in-slice:end/disp8
+701     # ++curr
+702     40/increment-EAX
+703     eb/jump  $skip-chars-matching-in-slice:loop/disp8
+704 $skip-chars-matching-in-slice:end:
+705     # . restore registers
+706     5b/pop-to-EBX
+707     5a/pop-to-EDX
+708     59/pop-to-ECX
+709     # . epilog
+710     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+711     5d/pop-to-EBP
+712     c3/return
+713 
+714 test-skip-chars-matching-in-slice:
+715     # (EAX..ECX) = "  ab"
+716     b8/copy-to-EAX  "  ab"/imm32
+717     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+718     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+719     05/add-to-EAX  4/imm32
+720     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
+721     # . . push args
+722     68/push  0x20/imm32/space
+723     51/push-ECX
+724     50/push-EAX
+725     # . . call
+726     e8/call  skip-chars-matching-in-slice/disp32
+727     # . . discard args
+728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+729     # check-ints-equal(ECX-EAX, 2, msg)
+730     # . . push args
+731     68/push  "F - test-skip-chars-matching-in-slice"/imm32
+732     68/push  2/imm32
+733     # . . push ECX-EAX
+734     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+735     51/push-ECX
+736     # . . call
+737     e8/call  check-ints-equal/disp32
+738     # . . discard args
+739     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+740     # end
+741     c3/return
+742 
+743 test-skip-chars-matching-in-slice-none:
+744     # (EAX..ECX) = "ab"
+745     b8/copy-to-EAX  "ab"/imm32
+746     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+747     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+748     05/add-to-EAX  4/imm32
+749     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
+750     # . . push args
+751     68/push  0x20/imm32/space
+752     51/push-ECX
+753     50/push-EAX
+754     # . . call
+755     e8/call  skip-chars-matching-in-slice/disp32
+756     # . . discard args
+757     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+758     # check-ints-equal(ECX-EAX, 2, msg)
+759     # . . push args
+760     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
+761     68/push  2/imm32
+762     # . . push ECX-EAX
+763     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+764     51/push-ECX
+765     # . . call
+766     e8/call  check-ints-equal/disp32
+767     # . . discard args
+768     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+769     # end
+770     c3/return
+771 
+772 # minor fork of 'skip-chars-matching-in-slice'
+773 skip-chars-not-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
+774     # . prolog
+775     55/push-EBP
+776     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+777     # . save registers
+778     51/push-ECX
+779     52/push-EDX
+780     53/push-EBX
+781     # EAX = curr
+782     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+783     # ECX = end
+784     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
+785     # EDX = delimiter
+786     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
+787     # EBX = 0
+788     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+789 $skip-chars-not-matching-in-slice:loop:
+790     # if (curr >= end) break
+791     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+792     73/jump-if-greater-or-equal-unsigned  $skip-chars-not-matching-in-slice:end/disp8
+793     # if (*curr == delimiter) break
+794     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
+795     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
+796     74/jump-if-equal  $skip-chars-not-matching-in-slice:end/disp8
+797     # ++curr
+798     40/increment-EAX
+799     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
+800 $skip-chars-not-matching-in-slice:end:
+801     # . restore registers
+802     5b/pop-to-EBX
+803     5a/pop-to-EDX
+804     59/pop-to-ECX
+805     # . epilog
+806     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+807     5d/pop-to-EBP
+808     c3/return
+809 
+810 test-skip-chars-not-matching-in-slice:
+811     # (EAX..ECX) = "ab "
+812     b8/copy-to-EAX  "ab "/imm32
+813     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+814     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+815     05/add-to-EAX  4/imm32
+816     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
+817     # . . push args
+818     68/push  0x20/imm32/space
+819     51/push-ECX
+820     50/push-EAX
+821     # . . call
+822     e8/call  skip-chars-not-matching-in-slice/disp32
+823     # . . discard args
+824     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+825     # check-ints-equal(ECX-EAX, 1, msg)
+826     # . . push args
+827     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
+828     68/push  1/imm32
+829     # . . push ECX-EAX
+830     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+831     51/push-ECX
+832     # . . call
+833     e8/call  check-ints-equal/disp32
+834     # . . discard args
+835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+836     # end
+837     c3/return
+838 
+839 test-skip-chars-not-matching-in-slice-none:
+840     # (EAX..ECX) = " ab"
+841     b8/copy-to-EAX  " ab"/imm32
+842     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+843     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+844     05/add-to-EAX  4/imm32
+845     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
+846     # . . push args
+847     68/push  0x20/imm32/space
+848     51/push-ECX
+849     50/push-EAX
+850     # . . call
+851     e8/call  skip-chars-not-matching-in-slice/disp32
+852     # . . discard args
+853     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+854     # check-ints-equal(ECX-EAX, 3, msg)
+855     # . . push args
+856     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
+857     68/push  3/imm32
+858     # . . push ECX-EAX
+859     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+860     51/push-ECX
+861     # . . call
+862     e8/call  check-ints-equal/disp32
+863     # . . discard args
+864     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+865     # end
+866     c3/return
+867 
+868 test-skip-chars-not-matching-in-slice-all:
+869     # (EAX..ECX) = "ab"
+870     b8/copy-to-EAX  "ab"/imm32
+871     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+872     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+873     05/add-to-EAX  4/imm32
+874     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
+875     # . . push args
+876     68/push  0x20/imm32/space
+877     51/push-ECX
+878     50/push-EAX
+879     # . . call
+880     e8/call  skip-chars-not-matching-in-slice/disp32
+881     # . . discard args
+882     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+883     # check-ints-equal(ECX-EAX, 0, msg)
+884     # . . push args
+885     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
+886     68/push  0/imm32
+887     # . . push ECX-EAX
+888     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+889     51/push-ECX
+890     # . . call
+891     e8/call  check-ints-equal/disp32
+892     # . . discard args
+893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+894     # end
+895     c3/return
+896 
+897 # . . vim:nowrap:textwidth=0
 
diff --git a/html/subx/074print-int-decimal.subx.html b/html/subx/074print-int-decimal.subx.html index cbfcbb8d..f84532a3 100644 --- a/html/subx/074print-int-decimal.subx.html +++ b/html/subx/074print-int-decimal.subx.html @@ -3,8 +3,8 @@ Mu - subx/074print-int-decimal.subx - - + + @@ -14,17 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.Constant { color: #008787; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.CommentedCode { color: #8a8a8a; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Folded { color: #080808; background-color: #949494; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.subxS2Comment { color: #8a8a8a; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.subxTest { color: #5f8700; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Folded { color: #080808; background-color: #949494; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -41,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -67,289 +66,281 @@ if ('onhashchange' in window) { 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 a single test, while debugging - 9 #? e8/call test-print-int32-decimal-negative/disp32 - 10 #? - 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 #? b8/copy-to-EAX 1/imm32/exit - 14 #? cd/syscall 0x80/imm8 - 15 - 16 print-int32-decimal: # out : (address stream), n : int32 - 17 # works by generating characters from lowest to highest and pushing them - 18 # to the stack, before popping them one by one into the stream - 19 # - 20 # pseudocode: - 21 # push sentinel - 22 # EAX = abs(n) - 23 # while true - 24 # sign-extend EAX into EDX - 25 # EAX, EDX = EAX/10, EAX%10 - 26 # EDX += '0' - 27 # push EDX - 28 # if (EAX == 0) break - 29 # if n < 0 - 30 # push '-' - 31 # w = out->write - 32 # curr = &out->data[out->write] - 33 # max = &out->data[out->length] - 34 # while true - 35 # pop into EAX - 36 # if (EAX == sentinel) break - 37 # if (curr >= max) abort - 38 # *curr = AL - 39 # ++curr - 40 # ++w - 41 # out->write = w - 42 # (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa) - 43 # (this pseudocode contains registers because operations like division - 44 # require specific registers in x86) - 45 # - 46 # . prolog - 47 55/push-EBP - 48 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 49 # . save registers - 50 50/push-EAX - 51 51/push-ECX - 52 52/push-EDX - 53 53/push-EBX - 54 57/push-EDI - 55 # ten/ECX = 10 - 56 b9/copy-to-ECX 0xa/imm32 - 57 # push sentinel - 58 68/push 0/imm32/sentinel - 59 # EAX = abs(n) - 60 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 61 3d/compare-EAX-with 0/imm32 - 62 7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8 - 63 $print-int32-decimal:negative: - 64 f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX - 65 $print-int32-decimal:read-loop: - 66 # EAX, EDX = EAX / 10, EAX % 10 - 67 99/sign-extend-EAX-into-EDX - 68 f7 7/subop/idiv 3/mod/direct 1/rm32/ECX . . . . . . # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX - 69 # EDX += '0' - 70 81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 0x30/imm32 # add to EDX - 71 # push EDX - 72 52/push-EDX - 73 # if (EAX == 0) break - 74 3d/compare-EAX-and 0/imm32 - 75 7f/jump-if-greater $print-int32-decimal:read-loop/disp8 - 76 $print-int32-decimal:read-break: - 77 # if (n < 0) push('-') - 78 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 0/imm32 # compare *(EBP+12) - 79 7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8 - 80 $print-int32-decimal:push-negative: - 81 68/push 0x2d/imm32/- - 82 $print-int32-decimal:write: - 83 # EDI = out - 84 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - 85 # w/EDX = out->write - 86 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX - 87 # curr/ECX = &out->data[out->write] - 88 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 1/r32/ECX 0xc/disp8 . # copy EBX+EDX+12 to ECX - 89 # max/EBX = &out->data[out->length] - 90 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 8/disp8 . # copy *(EDI+8) to EBX - 91 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 3/r32/EBX 0xc/disp8 . # copy EDI+EBX+12 to EBX - 92 $print-int32-decimal:write-loop: - 93 # pop into EAX - 94 58/pop-to-EAX - 95 # if (EAX == sentinel) break - 96 3d/compare-EAX-and 0/imm32/sentinel - 97 74/jump-if-equal $print-int32-decimal:write-break/disp8 - 98 # if (curr >= max) abort - 99 39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX with EBX -100 7d/jump-if-greater-or-equal $print-int32-decimal:abort/disp8 -101 $print-int32-decimal:write-char: -102 # *curr = AL -103 88/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy AL to byte at *ECX -104 # ++curr -105 41/increment-ECX -106 # ++w -107 42/increment-EDX -108 eb/jump $print-int32-decimal:write-loop/disp8 -109 $print-int32-decimal:write-break: -110 # out->write = w -111 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI -112 $print-int32-decimal:end: -113 # . restore registers -114 5f/pop-to-EDI -115 5b/pop-to-EBX -116 5a/pop-to-EDX -117 59/pop-to-ECX -118 58/pop-to-EAX -119 # . epilog -120 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -121 5d/pop-to-EBP -122 c3/return -123 -124 $print-int32-decimal:abort: -125 # . _write(2/stderr, error) -126 # . . push args -127 68/push "print-int32-decimal: out of space\n"/imm32 -128 68/push 2/imm32/stderr -129 # . . call -130 e8/call _write/disp32 -131 # . . discard args -132 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -133 # . syscall(exit, 1) -134 bb/copy-to-EBX 1/imm32 -135 b8/copy-to-EAX 1/imm32/exit -136 cd/syscall 0x80/imm8 -137 # never gets here -138 -139 test-print-int32-decimal: -140 # - check that a single-digit number converts correctly -141 # setup -142 # . clear-stream(_test-stream) -143 # . . push args + 8 print-int32-decimal: # out : (address stream), n : int32 + 9 # works by generating characters from lowest to highest and pushing them + 10 # to the stack, before popping them one by one into the stream + 11 # + 12 # pseudocode: + 13 # push sentinel + 14 # EAX = abs(n) + 15 # while true + 16 # sign-extend EAX into EDX + 17 # EAX, EDX = EAX/10, EAX%10 + 18 # EDX += '0' + 19 # push EDX + 20 # if (EAX == 0) break + 21 # if n < 0 + 22 # push '-' + 23 # w = out->write + 24 # curr = &out->data[out->write] + 25 # max = &out->data[out->length] + 26 # while true + 27 # pop into EAX + 28 # if (EAX == sentinel) break + 29 # if (curr >= max) abort + 30 # *curr = AL + 31 # ++curr + 32 # ++w + 33 # out->write = w + 34 # (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa) + 35 # (this pseudocode contains registers because operations like division + 36 # require specific registers in x86) + 37 # + 38 # . prolog + 39 55/push-EBP + 40 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 41 # . save registers + 42 50/push-EAX + 43 51/push-ECX + 44 52/push-EDX + 45 53/push-EBX + 46 57/push-EDI + 47 # ten/ECX = 10 + 48 b9/copy-to-ECX 0xa/imm32 + 49 # push sentinel + 50 68/push 0/imm32/sentinel + 51 # EAX = abs(n) + 52 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 53 3d/compare-EAX-with 0/imm32 + 54 7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8 + 55 $print-int32-decimal:negative: + 56 f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX + 57 $print-int32-decimal:read-loop: + 58 # EAX, EDX = EAX / 10, EAX % 10 + 59 99/sign-extend-EAX-into-EDX + 60 f7 7/subop/idiv 3/mod/direct 1/rm32/ECX . . . . . . # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX + 61 # EDX += '0' + 62 81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 0x30/imm32 # add to EDX + 63 # push EDX + 64 52/push-EDX + 65 # if (EAX == 0) break + 66 3d/compare-EAX-and 0/imm32 + 67 7f/jump-if-greater $print-int32-decimal:read-loop/disp8 + 68 $print-int32-decimal:read-break: + 69 # if (n < 0) push('-') + 70 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 0/imm32 # compare *(EBP+12) + 71 7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8 + 72 $print-int32-decimal:push-negative: + 73 68/push 0x2d/imm32/- + 74 $print-int32-decimal:write: + 75 # EDI = out + 76 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + 77 # w/EDX = out->write + 78 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX + 79 # curr/ECX = &out->data[out->write] + 80 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 1/r32/ECX 0xc/disp8 . # copy EBX+EDX+12 to ECX + 81 # max/EBX = &out->data[out->length] + 82 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 8/disp8 . # copy *(EDI+8) to EBX + 83 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 3/r32/EBX 0xc/disp8 . # copy EDI+EBX+12 to EBX + 84 $print-int32-decimal:write-loop: + 85 # pop into EAX + 86 58/pop-to-EAX + 87 # if (EAX == sentinel) break + 88 3d/compare-EAX-and 0/imm32/sentinel + 89 74/jump-if-equal $print-int32-decimal:write-break/disp8 + 90 # if (curr >= max) abort + 91 39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX with EBX + 92 73/jump-if-greater-or-equal-unsigned $print-int32-decimal:abort/disp8 + 93 $print-int32-decimal:write-char: + 94 # *curr = AL + 95 88/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy AL to byte at *ECX + 96 # ++curr + 97 41/increment-ECX + 98 # ++w + 99 42/increment-EDX +100 eb/jump $print-int32-decimal:write-loop/disp8 +101 $print-int32-decimal:write-break: +102 # out->write = w +103 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI +104 $print-int32-decimal:end: +105 # . restore registers +106 5f/pop-to-EDI +107 5b/pop-to-EBX +108 5a/pop-to-EDX +109 59/pop-to-ECX +110 58/pop-to-EAX +111 # . epilog +112 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +113 5d/pop-to-EBP +114 c3/return +115 +116 $print-int32-decimal:abort: +117 # . _write(2/stderr, error) +118 # . . push args +119 68/push "print-int32-decimal: out of space\n"/imm32 +120 68/push 2/imm32/stderr +121 # . . call +122 e8/call _write/disp32 +123 # . . discard args +124 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +125 # . syscall(exit, 1) +126 bb/copy-to-EBX 1/imm32 +127 b8/copy-to-EAX 1/imm32/exit +128 cd/syscall 0x80/imm8 +129 # never gets here +130 +131 test-print-int32-decimal: +132 # - check that a single-digit number converts correctly +133 # setup +134 # . clear-stream(_test-stream) +135 # . . push args +136 68/push _test-stream/imm32 +137 # . . call +138 e8/call clear-stream/disp32 +139 # . . discard args +140 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +141 # print-int32-decimal(_test-stream, 9) +142 # . . push args +143 68/push 9/imm32 144 68/push _test-stream/imm32 145 # . . call -146 e8/call clear-stream/disp32 +146 e8/call print-int32-decimal/disp32 147 # . . discard args -148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -149 # print-int32-decimal(_test-stream, 9) +148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +149 # check-stream-equal(_test-stream, "9", msg) 150 # . . push args -151 68/push 9/imm32 -152 68/push _test-stream/imm32 -153 # . . call -154 e8/call print-int32-decimal/disp32 -155 # . . discard args -156 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -157 # check-stream-equal(_test-stream, "9", msg) -158 # . . push args -159 68/push "F - test-print-int32-decimal"/imm32 -160 68/push "9"/imm32 -161 68/push _test-stream/imm32 -162 # . . call -163 e8/call check-stream-equal/disp32 -164 # . . discard args -165 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -166 # . end -167 c3/return -168 -169 test-print-int32-decimal-zero: -170 # - check that 0 converts correctly -171 # setup -172 # . clear-stream(_test-stream) -173 # . . push args +151 68/push "F - test-print-int32-decimal"/imm32 +152 68/push "9"/imm32 +153 68/push _test-stream/imm32 +154 # . . call +155 e8/call check-stream-equal/disp32 +156 # . . discard args +157 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +158 # . end +159 c3/return +160 +161 test-print-int32-decimal-zero: +162 # - check that 0 converts correctly +163 # setup +164 # . clear-stream(_test-stream) +165 # . . push args +166 68/push _test-stream/imm32 +167 # . . call +168 e8/call clear-stream/disp32 +169 # . . discard args +170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +171 # print-int32-decimal(_test-stream, 0) +172 # . . push args +173 68/push 0/imm32 174 68/push _test-stream/imm32 175 # . . call -176 e8/call clear-stream/disp32 +176 e8/call print-int32-decimal/disp32 177 # . . discard args -178 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -179 # print-int32-decimal(_test-stream, 0) +178 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +179 # check-stream-equal(_test-stream, "0", msg) 180 # . . push args -181 68/push 0/imm32 -182 68/push _test-stream/imm32 -183 # . . call -184 e8/call print-int32-decimal/disp32 -185 # . . discard args -186 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -187 # check-stream-equal(_test-stream, "0", msg) -188 # . . push args -189 68/push "F - test-print-int32-decimal-zero"/imm32 -190 68/push "0"/imm32 -191 68/push _test-stream/imm32 -192 # . . call -193 e8/call check-stream-equal/disp32 -194 # . . discard args -195 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -196 # . end -197 c3/return -198 -199 test-print-int32-decimal-multiple-digits: -200 # - check that a multi-digit number converts correctly -201 # setup -202 # . clear-stream(_test-stream) -203 # . . push args +181 68/push "F - test-print-int32-decimal-zero"/imm32 +182 68/push "0"/imm32 +183 68/push _test-stream/imm32 +184 # . . call +185 e8/call check-stream-equal/disp32 +186 # . . discard args +187 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +188 # . end +189 c3/return +190 +191 test-print-int32-decimal-multiple-digits: +192 # - check that a multi-digit number converts correctly +193 # setup +194 # . clear-stream(_test-stream) +195 # . . push args +196 68/push _test-stream/imm32 +197 # . . call +198 e8/call clear-stream/disp32 +199 # . . discard args +200 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +201 # print-int32-decimal(_test-stream, 10) +202 # . . push args +203 68/push 0xa/imm32 204 68/push _test-stream/imm32 205 # . . call -206 e8/call clear-stream/disp32 +206 e8/call print-int32-decimal/disp32 207 # . . discard args -208 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -209 # print-int32-decimal(_test-stream, 10) +208 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +209 # check-stream-equal(_test-stream, "10", msg) 210 # . . push args -211 68/push 0xa/imm32 -212 68/push _test-stream/imm32 -213 # . . call -214 e8/call print-int32-decimal/disp32 -215 # . . discard args -216 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -217 # check-stream-equal(_test-stream, "10", msg) -218 # . . push args -219 68/push "F - test-print-int32-decimal-multiple-digits"/imm32 -220 68/push "10"/imm32 -221 68/push _test-stream/imm32 -222 # . . call -223 e8/call check-stream-equal/disp32 -224 # . . discard args -225 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -226 # . end -227 c3/return -228 -229 test-print-int32-decimal-negative: -230 # - check that a negative single-digit number converts correctly -231 # setup -232 # . clear-stream(_test-stream) -233 # . . push args +211 68/push "F - test-print-int32-decimal-multiple-digits"/imm32 +212 68/push "10"/imm32 +213 68/push _test-stream/imm32 +214 # . . call +215 e8/call check-stream-equal/disp32 +216 # . . discard args +217 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +218 # . end +219 c3/return +220 +221 test-print-int32-decimal-negative: +222 # - check that a negative single-digit number converts correctly +223 # setup +224 # . clear-stream(_test-stream) +225 # . . push args +226 68/push _test-stream/imm32 +227 # . . call +228 e8/call clear-stream/disp32 +229 # . . discard args +230 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +231 # print-int32-decimal(_test-stream, -9) +232 # . . push args +233 68/push -9/imm32 234 68/push _test-stream/imm32 235 # . . call -236 e8/call clear-stream/disp32 +236 e8/call print-int32-decimal/disp32 237 # . . discard args -238 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -239 # print-int32-decimal(_test-stream, -9) -240 # . . push args -241 68/push -9/imm32 -242 68/push _test-stream/imm32 -243 # . . call -244 e8/call print-int32-decimal/disp32 -245 # . . discard args -246 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -247 +-- 26 lines: #? # dump _test-stream --------------------------------------------------------------------------------------------------------------------- -273 # check-stream-equal(_test-stream, "-9", msg) -274 # . . push args -275 68/push "F - test-print-int32-decimal-negative"/imm32 -276 68/push "-9"/imm32 -277 68/push _test-stream/imm32 -278 # . . call -279 e8/call check-stream-equal/disp32 -280 # . . discard args -281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -282 # . end -283 c3/return -284 -285 test-print-int32-decimal-negative-multiple-digits: -286 # - check that a multi-digit number converts correctly -287 # setup -288 # . clear-stream(_test-stream) -289 # . . push args +238 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +239 +-- 26 lines: #? # dump _test-stream --------------------------------------------------------------------------------------------------------------------- +265 # check-stream-equal(_test-stream, "-9", msg) +266 # . . push args +267 68/push "F - test-print-int32-decimal-negative"/imm32 +268 68/push "-9"/imm32 +269 68/push _test-stream/imm32 +270 # . . call +271 e8/call check-stream-equal/disp32 +272 # . . discard args +273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +274 # . end +275 c3/return +276 +277 test-print-int32-decimal-negative-multiple-digits: +278 # - check that a multi-digit number converts correctly +279 # setup +280 # . clear-stream(_test-stream) +281 # . . push args +282 68/push _test-stream/imm32 +283 # . . call +284 e8/call clear-stream/disp32 +285 # . . discard args +286 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +287 # print-int32-decimal(_test-stream, -10) +288 # . . push args +289 68/push -0xa/imm32 290 68/push _test-stream/imm32 291 # . . call -292 e8/call clear-stream/disp32 +292 e8/call print-int32-decimal/disp32 293 # . . discard args -294 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -295 # print-int32-decimal(_test-stream, -10) +294 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +295 # check-stream-equal(_test-stream, "-10", msg) 296 # . . push args -297 68/push -0xa/imm32 -298 68/push _test-stream/imm32 -299 # . . call -300 e8/call print-int32-decimal/disp32 -301 # . . discard args -302 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -303 # check-stream-equal(_test-stream, "-10", msg) -304 # . . push args -305 68/push "F - test-print-int32-decimal-negative-multiple-digits"/imm32 -306 68/push "-10"/imm32 -307 68/push _test-stream/imm32 -308 # . . call -309 e8/call check-stream-equal/disp32 -310 # . . discard args -311 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -312 # . end -313 c3/return -314 -315 # . . vim:nowrap:textwidth=0 +297 68/push "F - test-print-int32-decimal-negative-multiple-digits"/imm32 +298 68/push "-10"/imm32 +299 68/push _test-stream/imm32 +300 # . . call +301 e8/call check-stream-equal/disp32 +302 # . . discard args +303 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +304 # . end +305 c3/return +306 +307 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/075array-equal.subx.html b/html/subx/075array-equal.subx.html new file mode 100644 index 00000000..d40790ce --- /dev/null +++ b/html/subx/075array-equal.subx.html @@ -0,0 +1,694 @@ + + + + +Mu - subx/075array-equal.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/075array-equal.subx +
+  1 # Comparing arrays of numbers.
+  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:
+  9     # initialize heap
+ 10     # . Heap = new-segment(64KB)
+ 11     # . . push args
+ 12     68/push  Heap/imm32
+ 13     68/push  0x10000/imm32/64KB
+ 14     # . . call
+ 15     e8/call  new-segment/disp32
+ 16     # . . discard args
+ 17     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 18 
+ 19     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 20 $array-equal-main:end:
+ 21     # syscall(exit, Num-test-failures)
+ 22     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 23     b8/copy-to-EAX  1/imm32/exit
+ 24     cd/syscall  0x80/imm8
+ 25 
+ 26 array-equal?:  # a : (address array int), b : (address array int) -> EAX : boolean
+ 27     # pseudocode:
+ 28     #   lena = a->length
+ 29     #   if (lena != b->length) return false
+ 30     #   i = 0
+ 31     #   curra = a->data
+ 32     #   currb = b->data
+ 33     #   while i < lena
+ 34     #     i1 = *curra
+ 35     #     i2 = *currb
+ 36     #     if (c1 != c2) return false
+ 37     #     i+=4, curra+=4, currb+=4
+ 38     #   return true
+ 39     #
+ 40     # registers:
+ 41     #   i: ECX
+ 42     #   lena: EDX
+ 43     #   curra: ESI
+ 44     #   currb: EDI
+ 45     #   i1: EAX
+ 46     #   i2: EBX
+ 47     #
+ 48     # . prolog
+ 49     55/push-EBP
+ 50     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 51     # . save registers
+ 52     51/push-ECX
+ 53     52/push-EDX
+ 54     53/push-EBX
+ 55     56/push-ESI
+ 56     57/push-EDI
+ 57     # ESI = a
+ 58     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 59     # EDI = b
+ 60     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+ 61     # lena/EDX = a->length
+ 62     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 63 $array-equal?:lengths:
+ 64     # if (lena != b->length) return false
+ 65     39/compare                      0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare *EDI and EDX
+ 66     75/jump-if-not-equal  $array-equal?:false/disp8
+ 67     # curra/ESI = a->data
+ 68     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               4/imm32           # add to ESI
+ 69     # currb/EDI = b->data
+ 70     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
+ 71     # i/ECX = i1/EAX = i2/EBX = 0
+ 72     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+ 73 $array-equal?:loop:
+ 74     # if (i >= lena) return true
+ 75     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 76     7d/jump-if-greater-or-equal  $array-equal?:true/disp8
+ 77     # i1 = *curra
+ 78     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+ 79     # i2 = *currb
+ 80     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/EBX   .               .                 # copy *EDI to EBX
+ 81     # if (i1 != i2) return false
+ 82     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
+ 83     75/jump-if-not-equal  $array-equal?:false/disp8
+ 84     # i += 4
+ 85     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm32           # add to ECX
+ 86     # currs += 4
+ 87     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               4/imm32           # add to ESI
+ 88     # currb += 4
+ 89     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
+ 90     eb/jump  $array-equal?:loop/disp8
+ 91 $array-equal?:true:
+ 92     b8/copy-to-EAX  1/imm32
+ 93     eb/jump  $array-equal?:end/disp8
+ 94 $array-equal?:false:
+ 95     b8/copy-to-EAX  0/imm32
+ 96 $array-equal?:end:
+ 97     # . restore registers
+ 98     5f/pop-to-EDI
+ 99     5e/pop-to-ESI
+100     5b/pop-to-EBX
+101     5a/pop-to-EDX
+102     59/pop-to-ECX
+103     # . epilog
+104     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+105     5d/pop-to-EBP
+106     c3/return
+107 
+108 test-compare-empty-with-empty-array:
+109     # . prolog
+110     55/push-EBP
+111     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+112     # var ECX = []
+113     68/push  0/imm32/size
+114     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+115     # var EDX = []
+116     68/push  0/imm32/size
+117     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+118     # EAX = array-equal?(ECX, EDX)
+119     # . . push args
+120     52/push-EDX
+121     51/push-ECX
+122     # . . call
+123     e8/call  array-equal?/disp32
+124     # . . discard args
+125     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+126     # check-ints-equal(EAX, 1, msg)
+127     # . . push args
+128     68/push  "F - test-compare-empty-with-empty-array"/imm32
+129     68/push  1/imm32/true
+130     50/push-EAX
+131     # . . call
+132     e8/call  check-ints-equal/disp32
+133     # . . discard args
+134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+135     # . epilog
+136     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+137     5d/pop-to-EBP
+138     c3/return
+139 
+140 test-compare-empty-with-non-empty-array:  # also checks length-mismatch code path
+141     # . prolog
+142     55/push-EBP
+143     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+144     # var ECX = [1]
+145     68/push  1/imm32
+146     68/push  4/imm32/size
+147     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+148     # var EDX = []
+149     68/push  0/imm32/size
+150     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+151     # EAX = array-equal?(ECX, EDX)
+152     # . . push args
+153     52/push-EDX
+154     51/push-ECX
+155     # . . call
+156     e8/call  array-equal?/disp32
+157     # . . discard args
+158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+159     # check-ints-equal(EAX, 0, msg)
+160     # . . push args
+161     68/push  "F - test-compare-empty-with-non-empty-array"/imm32
+162     68/push  0/imm32/false
+163     50/push-EAX
+164     # . . call
+165     e8/call  check-ints-equal/disp32
+166     # . . discard args
+167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+168     # . epilog
+169     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+170     5d/pop-to-EBP
+171     c3/return
+172 
+173 test-compare-equal-arrays:
+174     # . prolog
+175     55/push-EBP
+176     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+177     # var ECX = [1, 2, 3]
+178     68/push  3/imm32
+179     68/push  2/imm32
+180     68/push  1/imm32
+181     68/push  0xc/imm32/size
+182     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+183     # var EDX = [1, 2, 3]
+184     68/push  3/imm32
+185     68/push  2/imm32
+186     68/push  1/imm32
+187     68/push  0xc/imm32/size
+188     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+189     # EAX = array-equal?(ECX, EDX)
+190     # . . push args
+191     52/push-EDX
+192     51/push-ECX
+193     # . . call
+194     e8/call  array-equal?/disp32
+195     # . . discard args
+196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+197     # check-ints-equal(EAX, 1, msg)
+198     # . . push args
+199     68/push  "F - test-compare-equal-arrays"/imm32
+200     68/push  1/imm32/true
+201     50/push-EAX
+202     # . . call
+203     e8/call  check-ints-equal/disp32
+204     # . . discard args
+205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+206     # . epilog
+207     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+208     5d/pop-to-EBP
+209     c3/return
+210 
+211 test-compare-inequal-arrays-equal-lengths:
+212     # . prolog
+213     55/push-EBP
+214     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+215     # var ECX = [1, 4, 3]
+216     68/push  3/imm32
+217     68/push  4/imm32
+218     68/push  1/imm32
+219     68/push  0xc/imm32/size
+220     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+221     # var EDX = [1, 2, 3]
+222     68/push  3/imm32
+223     68/push  2/imm32
+224     68/push  1/imm32
+225     68/push  0xc/imm32/size
+226     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+227     # EAX = array-equal?(ECX, EDX)
+228     # . . push args
+229     52/push-EDX
+230     51/push-ECX
+231     # . . call
+232     e8/call  array-equal?/disp32
+233     # . . discard args
+234     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+235     # check-ints-equal(EAX, 0, msg)
+236     # . . push args
+237     68/push  "F - test-compare-inequal-arrays-equal-lengths"/imm32
+238     68/push  0/imm32/false
+239     50/push-EAX
+240     # . . call
+241     e8/call  check-ints-equal/disp32
+242     # . . discard args
+243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+244     # . epilog
+245     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+246     5d/pop-to-EBP
+247     c3/return
+248 
+249 parse-array-of-ints:  # ad : (address allocation-descriptor), s : (address string) -> result/EAX : (address array int)
+250     # pseudocode
+251     #   end = s->data + s->length
+252     #   curr = s->data
+253     #   size = 0
+254     #   while true
+255     #     if (curr >= end) break
+256     #     curr = skip-chars-matching-in-slice(curr, end, ' ')
+257     #     if (curr >= end) break
+258     #     curr = skip-chars-not-matching-in-slice(curr, end, ' ')
+259     #     ++size
+260     #   result = allocate(ad, (size+1)*4)
+261     #   result->size = (size+1)*4
+262     #   var slice = {s->data, 0}
+263     #   out = result->data
+264     #   while true
+265     #     if (slice->start >= end) break
+266     #     slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
+267     #     if (slice->start >= end) break
+268     #     slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
+269     #     *out = parse-hex-int(slice)
+270     #     out += 4
+271     #     slice->start = slice->end
+272     #   return result
+273     #
+274     # . prolog
+275     55/push-EBP
+276     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+277     # . save registers
+278     51/push-ECX
+279     52/push-EDX
+280     53/push-EBX
+281     56/push-ESI
+282     57/push-EDI
+283     # ESI = s
+284     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+285     # curr/ECX = s->data
+286     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ESI+4 to ECX
+287     # end/EDX = s->data + s->length
+288     # . EDX = s->length
+289     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+290     # . EDX += curr
+291     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           1/r32/ECX   .               .                 # add ECX to EDX
+292     # size/EBX = 0
+293     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+294 $parse-array-of-ints:loop1:
+295     # if (curr >= end) break
+296     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+297     73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:break1/disp8
+298     # curr = skip-chars-matching-in-slice(curr, end, ' ')
+299     # . EAX = skip-chars-matching-in-slice(curr, end, ' ')
+300     # . . push args
+301     68/push  0x20/imm32/space
+302     52/push-EDX
+303     51/push-ECX
+304     # . . call
+305     e8/call  skip-chars-matching-in-slice/disp32
+306     # . . discard args
+307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+308     # . ECX = EAX
+309     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+310     # if (curr >= end) break
+311     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+312     73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:break1/disp8
+313     # curr = skip-chars-not-matching-in-slice(curr, end, ' ')
+314     # . EAX = skip-chars-not-matching-in-slice(curr, end, ' ')
+315     # . . push args
+316     68/push  0x20/imm32/space
+317     52/push-EDX
+318     51/push-ECX
+319     # . . call
+320     e8/call  skip-chars-not-matching-in-slice/disp32
+321     # . . discard args
+322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+323     # . ECX = EAX
+324     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+325     # size += 4
+326     81          0/subop/add         3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm32           # add to EBX
+327     eb/jump  $parse-array-of-ints:loop1/disp8
+328 $parse-array-of-ints:break1:
+329     # result/EDI = allocate(ad, size+4)
+330     # . EAX = allocate(ad, size+4)
+331     # . . push args
+332     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to EAX
+333     05/add-to-EAX  4/imm32
+334     50/push-EAX
+335     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+336     # . . call
+337     e8/call  allocate/disp32
+338     # . . discard args
+339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+340     # . EDI = EAX
+341     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
+342     # result->size = size
+343     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to *EAX
+344 $parse-array-of-ints:pass2:
+345     # var slice/ECX = {s->data, 0}
+346     # . push 0
+347     68/push  0/imm32/end
+348     # . push s->data
+349     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ESI+4 to ECX
+350     51/push-ECX
+351     # . bookmark
+352     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+353     # out/EBX = result->data
+354     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   4/disp8         .                 # copy EAX+4 to EBX
+355 $parse-array-of-ints:loop2:
+356     # if (slice->start >= end) break
+357     39/compare                      0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare *ECX with EDX
+358     73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:end/disp8
+359     # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
+360     # . EAX = skip-chars-matching-in-slice(slice->start, end, ' ')
+361     # . . push args
+362     68/push  0x20/imm32/space
+363     52/push-EDX
+364     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+365     # . . call
+366     e8/call  skip-chars-matching-in-slice/disp32
+367     # . . discard args
+368     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+369     # . slice->start = EAX
+370     89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ECX
+371     # if (slice->start >= end) break
+372     39/compare                      0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare *ECX with EDX
+373     73/jump-if-greater-or-equal-unsigned  $parse-array-of-ints:end/disp8
+374     # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
+375     # . EAX = skip-chars-not-matching-in-slice(curr, end, ' ')
+376     # . . push args
+377     68/push  0x20/imm32/space
+378     52/push-EDX
+379     50/push-EAX
+380     # . . call
+381     e8/call  skip-chars-not-matching-in-slice/disp32
+382     # . . discard args
+383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+384     # . slice->end = EAX
+385     89/copy                         1/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ECX+4)
+386     # *out = parse-hex-int(slice)
+387     # . EAX = parse-hex-int(slice)
+388     # . . push args
+389     51/push-ECX
+390     # . . call
+391     e8/call  parse-hex-int/disp32
+392     # . . discard args
+393     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+394     # *out = EAX
+395     89/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EBX
+396     # out += 4
+397     81          0/subop/add         3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm32           # add to EBX
+398     # slice->start = slice->end
+399     8b/copy                         1/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+400     89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ECX
+401     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm32           # add to ECX
+402     eb/jump  $parse-array-of-ints:loop2/disp8
+403 $parse-array-of-ints:end:
+404     # return EDI
+405     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy EDI to EAX
+406     # . reclaim locals
+407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+408     # . restore registers
+409     5f/pop-to-EDI
+410     5e/pop-to-ESI
+411     5b/pop-to-EBX
+412     5a/pop-to-EDX
+413     59/pop-to-ECX
+414     # . epilog
+415     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+416     5d/pop-to-EBP
+417     c3/return
+418 
+419 test-parse-array-of-ints:
+420     # . prolog
+421     55/push-EBP
+422     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+423     # var ECX = [1, 2, 3]
+424     68/push  3/imm32
+425     68/push  2/imm32
+426     68/push  1/imm32
+427     68/push  0xc/imm32/size
+428     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+429     # EAX = parse-array-of-ints(Heap, "1 2 3")
+430     # . . push args
+431     68/push  "1 2 3"/imm32
+432     68/push  Heap/imm32
+433     # . . call
+434     e8/call  parse-array-of-ints/disp32
+435     # . . discard args
+436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+437     # EAX = array-equal?(ECX, EAX)
+438     # . . push args
+439     50/push-EAX
+440     51/push-ECX
+441     # . . call
+442     e8/call  array-equal?/disp32
+443     # . . discard args
+444     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+445     # check-ints-equal(EAX, 1, msg)
+446     # . . push args
+447     68/push  "F - test-parse-array-of-ints"/imm32
+448     68/push  1/imm32/true
+449     50/push-EAX
+450     # . . call
+451     e8/call  check-ints-equal/disp32
+452     # . . discard args
+453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+454     # . epilog
+455     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+456     5d/pop-to-EBP
+457     c3/return
+458 
+459 test-parse-array-of-ints-empty:
+460     # - empty string = empty array
+461     # . prolog
+462     55/push-EBP
+463     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+464     # EAX = parse-array-of-ints(Heap, "")
+465     # . . push args
+466     68/push  ""/imm32
+467     68/push  Heap/imm32
+468     # . . call
+469     e8/call  parse-array-of-ints/disp32
+470     # . . discard args
+471     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+472     # check-ints-equal(*EAX, 0, msg)
+473     # . . push args
+474     68/push  "F - test-parse-array-of-ints-empty"/imm32
+475     68/push  0/imm32/size
+476     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
+477     # . . call
+478     e8/call  check-ints-equal/disp32
+479     # . . discard args
+480     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+481     # . epilog
+482     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+483     5d/pop-to-EBP
+484     c3/return
+485 
+486 test-parse-array-of-ints-just-whitespace:
+487     # - just whitespace = empty array
+488     # . prolog
+489     55/push-EBP
+490     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+491     # EAX = parse-array-of-ints(Heap, " ")
+492     # . . push args
+493     68/push  " "/imm32
+494     68/push  Heap/imm32
+495     # . . call
+496     e8/call  parse-array-of-ints/disp32
+497     # . . discard args
+498     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+499     # check-ints-equal(*EAX, 0, msg)
+500     # . . push args
+501     68/push  "F - test-parse-array-of-ints-empty"/imm32
+502     68/push  0/imm32/size
+503     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
+504     # . . call
+505     e8/call  check-ints-equal/disp32
+506     # . . discard args
+507     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+508     # . epilog
+509     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+510     5d/pop-to-EBP
+511     c3/return
+512 
+513 test-parse-array-of-ints-extra-whitespace:
+514     # . prolog
+515     55/push-EBP
+516     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+517     # var ECX = [1, 2, 3]
+518     68/push  3/imm32
+519     68/push  2/imm32
+520     68/push  1/imm32
+521     68/push  0xc/imm32/size
+522     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+523     # EAX = parse-array-of-ints(Heap, " 1 2  3  ")
+524     # . . push args
+525     68/push  " 1 2  3  "/imm32
+526     68/push  Heap/imm32
+527     # . . call
+528     e8/call  parse-array-of-ints/disp32
+529     # . . discard args
+530     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+531     # EAX = array-equal?(ECX, EAX)
+532     # . . push args
+533     50/push-EAX
+534     51/push-ECX
+535     # . . call
+536     e8/call  array-equal?/disp32
+537     # . . discard args
+538     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+539     # check-ints-equal(EAX, 1, msg)
+540     # . . push args
+541     68/push  "F - test-parse-array-of-ints-extra-whitespace"/imm32
+542     68/push  1/imm32/true
+543     50/push-EAX
+544     # . . call
+545     e8/call  check-ints-equal/disp32
+546     # . . discard args
+547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+548     # . epilog
+549     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+550     5d/pop-to-EBP
+551     c3/return
+552 
+553 # helper for later tests
+554 # compare an array with a string representation of an array literal
+555 check-array-equal:  # a : (address array int), expected : (address string), msg : (address string)
+556     # . prolog
+557     55/push-EBP
+558     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+559     # . save registers
+560     50/push-EAX
+561     # var b/ECX = parse-array-of-ints(Heap, expected)
+562     # . EAX = parse-array-of-ints(Heap, expected)
+563     # . . push args
+564     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+565     68/push  Heap/imm32
+566     # . . call
+567     e8/call  parse-array-of-ints/disp32
+568     # . . discard args
+569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+570     # . b = EAX
+571     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+572     # EAX = array-equal?(a, b)
+573     # . . push args
+574     51/push-ECX
+575     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+576     # . . call
+577     e8/call  array-equal?/disp32
+578     # . . discard args
+579     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+580     # check-ints-equal(EAX, 1, msg)
+581     # . . push args
+582     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+583     68/push  1/imm32
+584     50/push-EAX
+585     # . . call
+586     e8/call  check-ints-equal/disp32
+587     # . . discard args
+588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+589 $check-array-equal:end:
+590     # . restore registers
+591     58/pop-to-EAX
+592     # . epilog
+593     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+594     5d/pop-to-EBP
+595     c3/return
+596 
+597 test-check-array-equal:
+598     # . prolog
+599     55/push-EBP
+600     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+601     # var ECX = [1, 2, 3]
+602     68/push  3/imm32
+603     68/push  2/imm32
+604     68/push  1/imm32
+605     68/push  0xc/imm32/size
+606     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+607     # check-array-equal(ECX, "1 2 3", "msg")
+608     # . . push args
+609     68/push  "F - test-check-array-equal"/imm32
+610     68/push  "1 2 3"/imm32
+611     51/push-ECX
+612     # . . call
+613     e8/call  check-array-equal/disp32
+614     # . . discard args
+615     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+616     # . epilog
+617     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+618     5d/pop-to-EBP
+619     c3/return
+620 
+621 == data
+622 
+623 Heap:
+624   # curr
+625   0/imm32
+626   # limit
+627   0/imm32
+628 
+629 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/076zero-out.subx.html b/html/subx/076zero-out.subx.html new file mode 100644 index 00000000..fcc42479 --- /dev/null +++ b/html/subx/076zero-out.subx.html @@ -0,0 +1,147 @@ + + + + +Mu - subx/076zero-out.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/076zero-out.subx +
+ 1 # Fill a region of memory with zeroes.
+ 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 zero-out:  # start : address, len : int
+ 9     # pseudocode:
+10     #   curr/ESI = start
+11     #   i/ECX = 0
+12     #   while true
+13     #     if (i >= len) break
+14     #     *curr = 0
+15     #     ++curr
+16     #     ++i
+17     #
+18     # . prolog
+19     55/push-EBP
+20     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+21     # . save registers
+22     50/push-EAX
+23     51/push-ECX
+24     52/push-EDX
+25     56/push-ESI
+26     # curr/ESI = start
+27     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+28     # i/ECX = 0
+29     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+30     # EDX = len
+31     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+32 $zero-out:loop:
+33     # if (i >= len) break
+34     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+35     7d/jump-if-greater-or-equal  $zero-out:end/disp8
+36     # *curr = 0
+37     c6          0/subop/copy        0/mod/direct    6/rm32/ESI    .           .             .           .           .               0/imm8            # copy byte to *ESI
+38     # ++curr
+39     46/increment-ESI
+40     # ++i
+41     41/increment-ECX
+42     eb/jump  $zero-out:loop/disp8
+43 $zero-out:end:
+44     # . restore registers
+45     5e/pop-to-ESI
+46     5a/pop-to-EDX
+47     59/pop-to-ECX
+48     58/pop-to-EAX
+49     # . epilog
+50     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+51     5d/pop-to-EBP
+52     c3/return
+53 
+54 test-zero-out:
+55     # . prolog
+56     55/push-EBP
+57     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+58     # region/ECX = 34, 35, 36, 37
+59     68/push  0x37363534/imm32
+60     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+61     # zero-out(ECX, 3)
+62     # . . push args
+63     68/push  3/imm32/len
+64     51/push-ECX
+65     # . . call
+66     e8/call  zero-out/disp32
+67     # . . discard args
+68     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+69     # first 3 bytes cleared, fourth left alone
+70     # . check-ints-equal(*ECX, 0x37000000, msg)
+71     # . . push args
+72     68/push  "F - test-zero-out"/imm32
+73     68/push  0x37000000/imm32
+74     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+75     # . . call
+76     e8/call  check-ints-equal/disp32
+77     # . . discard args
+78     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+79     # . epilog
+80     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+81     5d/pop-to-EBP
+82     c3/return
+83 
+84 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/apps/assort.subx.html b/html/subx/apps/assort.subx.html index e05b7de6..8c5ec581 100644 --- a/html/subx/apps/assort.subx.html +++ b/html/subx/apps/assort.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/assort.subx - - + + @@ -14,19 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } .Folded { color: #080808; background-color: #949494; } +.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.SpecialChar { color: #d70000; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -43,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -62,1185 +60,671 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/apps/assort.subx
-   1 # Read a series of segments from stdin and concatenate segments with the same
-   2 # name on stdout.
-   3 #
-   4 # Segments are emitted in order of first encounter.
-   5 #
-   6 # Drop lines that are all comments. They could get misleading after assortment
-   7 # because we don't know if they refer to the line above or the line below.
-   8 #
-   9 # To run (from the subx/ directory):
-  10 #   $ ./subx translate *.subx apps/assort.subx -o apps/assort
-  11 #   $ cat x
-  12 #   == code
-  13 #   abc
-  14 #   == code
-  15 #   def
-  16 #   $ cat x  |./subx run apps/assort
-  17 #   == code
-  18 #   abc
-  19 #   def
-  20 
-  21 == code
-  22 #   instruction                     effective address                                                   register    displacement    immediate
-  23 # . op          subop               mod             rm32          base        index         scale       r32
-  24 # . 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
-  25 
-  26 Entry:
-  27     # initialize heap
-  28     # . Heap = new-segment(64KB)
-  29     # . . push args
-  30     68/push  Heap/imm32
-  31     68/push  0x10000/imm32/64KB
-  32     # . . call
-  33     e8/call  new-segment/disp32
-  34     # . . discard args
-  35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-  36 
-  37     # for debugging: run a single test
-  38 #?     e8/call test-convert/disp32
-  39 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
-  40 #?     eb/jump  $main:end/disp8
-  41 
-  42     # run tests if necessary, convert stdin if not
-  43     # . prolog
-  44     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-  45     # - if argc > 1 and argv[1] == "test", then return run_tests()
-  46     # . argc > 1
-  47     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
-  48     7e/jump-if-lesser-or-equal  $run-main/disp8
-  49     # . argv[1] == "test"
-  50     # . . push args
-  51     68/push  "test"/imm32
-  52     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-  53     # . . call
-  54     e8/call  kernel-string-equal?/disp32
-  55     # . . discard args
-  56     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-  57     # . check result
-  58     3d/compare-EAX-and  1/imm32
-  59     75/jump-if-not-equal  $run-main/disp8
-  60     # . run-tests()
-  61     e8/call  run-tests/disp32
-  62     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
-  63     eb/jump  $main:end/disp8
-  64 $run-main:
-  65     # - otherwise convert stdin
-  66     # var ed/EAX : exit-descriptor
-  67     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
-  68     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
-  69     # configure ed to really exit()
-  70     # . ed->target = 0
-  71     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-  72     # return convert(Stdin, 1/stdout, 2/stderr, ed)
-  73     # . . push args
-  74     50/push-EAX/ed
-  75     68/push  Stderr/imm32
-  76     68/push  Stdout/imm32
-  77     68/push  Stdin/imm32
-  78     # . . call
-  79     e8/call  convert/disp32
-  80     # . . discard args
-  81     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-  82     # . syscall(exit, 0)
-  83     bb/copy-to-EBX  0/imm32
-  84 $main:end:
-  85     b8/copy-to-EAX  1/imm32/exit
-  86     cd/syscall  0x80/imm8
-  87 
-  88 # data structure:
-  89 #   row: pair of (address array byte) and (address stream byte)
-  90 #   table: (address stream row)
-  91 
-  92 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
-  93     # pseudocode:
-  94     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
-  95     #   read-segments(in, table)
-  96     #   write-segments(out, table)
-  97     #
-  98     # . prolog
-  99     55/push-EBP
- 100     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 101     # . save registers
- 102     51/push-ECX
- 103     # var table/ECX : (address stream byte) = stream(10 * 8)
- 104     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x50/imm32        # subtract from ESP
- 105     68/push  0x50/imm32/length
- 106     68/push  0/imm32/read
- 107     68/push  0/imm32/write
- 108     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 109     # clear-stream(table)
- 110     # . . push args
- 111     51/push-ECX
- 112     # . . call
- 113     e8/call  clear-stream/disp32
- 114     # . . discard args
- 115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 116 $convert:read:
- 117     # read-segments(in, table)
- 118     # . . push args
- 119     51/push-ECX
- 120     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 121     # . . call
- 122     e8/call  read-segments/disp32
- 123     # . . discard args
- 124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 125 $convert:write:
- 126     # write-segments(out, table)
- 127     # . . push args
- 128     51/push-ECX
- 129     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 130     # . . call
- 131     e8/call  write-segments/disp32
- 132     # . . discard args
- 133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 134 $convert:end:
- 135     # . reclaim locals
- 136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
- 137     # . restore registers
- 138     59/pop-to-ECX
- 139     # . epilog
- 140     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 141     5d/pop-to-EBP
- 142     c3/return
- 143 
- 144 test-convert:
- 145     # . prolog
- 146     55/push-EBP
- 147     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 148     # setup
- 149     # . clear-stream(_test-input-stream)
- 150     # . . push args
- 151     68/push  _test-input-stream/imm32
- 152     # . . call
- 153     e8/call  clear-stream/disp32
- 154     # . . discard args
- 155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 156     # . clear-stream(_test-input-buffered-file+4)
- 157     # . . push args
- 158     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 159     05/add-to-EAX  4/imm32
- 160     50/push-EAX
- 161     # . . call
- 162     e8/call  clear-stream/disp32
- 163     # . . discard args
- 164     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 165     # . clear-stream(_test-output-stream)
- 166     # . . push args
- 167     68/push  _test-output-stream/imm32
- 168     # . . call
- 169     e8/call  clear-stream/disp32
- 170     # . . discard args
- 171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 172     # . clear-stream(_test-output-buffered-file+4)
- 173     # . . push args
- 174     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 175     05/add-to-EAX  4/imm32
- 176     50/push-EAX
- 177     # . . call
- 178     e8/call  clear-stream/disp32
- 179     # . . discard args
- 180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 181     # initialize input (meta comments in parens)
- 182     #   # comment 1
- 183     #     # comment 2 indented
- 184     #   == code 0x09000000  (new segment)
- 185     #   # comment 3 inside a segment
- 186     #   1
- 187     #                         (empty line)
- 188     #   2 3 # comment 4 inline with other contents
- 189     #   == data 0x0a000000  (new segment)
- 190     #   4 5/imm32
- 191     #   == code  (existing segment but non-contiguous with previous iteration)
- 192     #   6 7
- 193     #   8 9  (multiple lines)
- 194     #   == code  (existing segment contiguous with previous iteration)
- 195     #   10 11
- 196     # . write(_test-input-stream, "# comment 1\n")
- 197     # . . push args
- 198     68/push  "# comment 1\n"/imm32
- 199     68/push  _test-input-stream/imm32
- 200     # . . call
- 201     e8/call  write/disp32
- 202     # . . discard args
- 203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 204     # . write(_test-input-stream, "  # comment 2 indented\n")
- 205     # . . push args
- 206     68/push  "  # comment 2 indented\n"/imm32
- 207     68/push  _test-input-stream/imm32
- 208     # . . call
- 209     e8/call  write/disp32
- 210     # . . discard args
- 211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 212     # . write(_test-input-stream, "== code 0x09000000\n")
- 213     # . . push args
- 214     68/push  "== code 0x09000000\n"/imm32
- 215     68/push  _test-input-stream/imm32
- 216     # . . call
- 217     e8/call  write/disp32
- 218     # . . discard args
- 219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 220     # . write(_test-input-stream, "# comment 3 inside a segment\n")
- 221     # . . push args
- 222     68/push  "# comment 3 inside a segment\n"/imm32
- 223     68/push  _test-input-stream/imm32
- 224     # . . call
- 225     e8/call  write/disp32
- 226     # . . discard args
- 227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 228     # . write(_test-input-stream, "1\n")
- 229     # . . push args
- 230     68/push  "1\n"/imm32
- 231     68/push  _test-input-stream/imm32
- 232     # . . call
- 233     e8/call  write/disp32
- 234     # . . discard args
- 235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 236     # . write(_test-input-stream, "\n")  # empty line
- 237     # . . push args
- 238     68/push  "\n"/imm32
- 239     68/push  _test-input-stream/imm32
- 240     # . . call
- 241     e8/call  write/disp32
- 242     # . . discard args
- 243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 244     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
- 245     # . . push args
- 246     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
- 247     68/push  _test-input-stream/imm32
- 248     # . . call
- 249     e8/call  write/disp32
- 250     # . . discard args
- 251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 252     # . write(_test-input-stream, "== data 0x0a000000\n")
- 253     # . . push args
- 254     68/push  "== data 0x0a000000\n"/imm32
- 255     68/push  _test-input-stream/imm32
- 256     # . . call
- 257     e8/call  write/disp32
- 258     # . . discard args
- 259     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 260     # . write(_test-input-stream, "4 5/imm32\n")
- 261     # . . push args
- 262     68/push  "4 5/imm32\n"/imm32
- 263     68/push  _test-input-stream/imm32
- 264     # . . call
- 265     e8/call  write/disp32
- 266     # . . discard args
- 267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 268     # . write(_test-input-stream, "== code\n")
- 269     # . . push args
- 270     68/push  "== code\n"/imm32
- 271     68/push  _test-input-stream/imm32
- 272     # . . call
- 273     e8/call  write/disp32
- 274     # . . discard args
- 275     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 276     # . write(_test-input-stream, "6 7\n")
- 277     # . . push args
- 278     68/push  "6 7\n"/imm32
- 279     68/push  _test-input-stream/imm32
- 280     # . . call
- 281     e8/call  write/disp32
- 282     # . . discard args
- 283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 284     # . write(_test-input-stream, "8 9\n")
- 285     # . . push args
- 286     68/push  "8 9\n"/imm32
- 287     68/push  _test-input-stream/imm32
- 288     # . . call
- 289     e8/call  write/disp32
- 290     # . . discard args
- 291     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 292     # . write(_test-input-stream, "== code\n")
- 293     # . . push args
- 294     68/push  "== code\n"/imm32
- 295     68/push  _test-input-stream/imm32
- 296     # . . call
- 297     e8/call  write/disp32
- 298     # . . discard args
- 299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 300     # . write(_test-input-stream, "10 11\n")
- 301     # . . push args
- 302     68/push  "10 11\n"/imm32
- 303     68/push  _test-input-stream/imm32
- 304     # . . call
- 305     e8/call  write/disp32
- 306     # . . discard args
- 307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 308     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 309     # . . push args
- 310     68/push  _test-output-buffered-file/imm32
- 311     68/push  _test-input-buffered-file/imm32
- 312     # . . call
- 313     e8/call  convert/disp32
- 314     # . . discard args
- 315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 316     # . flush(_test-output-buffered-file)
- 317     # . . push args
- 318     68/push  _test-output-buffered-file/imm32
- 319     # . . call
- 320     e8/call  flush/disp32
- 321     # . . discard args
- 322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 323     # check output
- 324     #   == code 0x09000000
- 325     #   1
- 326     #   2 3 # comment 4 inline with other contents
- 327     #   6 7
- 328     #   8 9
- 329     #   10 11
- 330     #   == data 0x0a000000
- 331     #   4 5/imm32
- 332 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
- 365     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
- 366     # . . push args
- 367     68/push  "F - test-convert/0"/imm32
- 368     68/push  "== code 0x09000000"/imm32
- 369     68/push  _test-output-stream/imm32
- 370     # . . call
- 371     e8/call  check-next-stream-line-equal/disp32
- 372     # . . discard args
- 373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 374     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
- 375     # . . push args
- 376     68/push  "F - test-convert/1"/imm32
- 377     68/push  "1"/imm32
- 378     68/push  _test-output-stream/imm32
- 379     # . . call
- 380     e8/call  check-next-stream-line-equal/disp32
- 381     # . . discard args
- 382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 383     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
- 384     # . . push args
- 385     68/push  "F - test-convert/2"/imm32
- 386     68/push  "2 3 # comment 4 inline with other contents"/imm32
- 387     68/push  _test-output-stream/imm32
- 388     # . . call
- 389     e8/call  check-next-stream-line-equal/disp32
- 390     # . . discard args
- 391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 392     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
- 393     # . . push args
- 394     68/push  "F - test-convert/3"/imm32
- 395     68/push  "6 7"/imm32
- 396     68/push  _test-output-stream/imm32
- 397     # . . call
- 398     e8/call  check-next-stream-line-equal/disp32
- 399     # . . discard args
- 400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 401     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
- 402     # . . push args
- 403     68/push  "F - test-convert/4"/imm32
- 404     68/push  "8 9"/imm32
- 405     68/push  _test-output-stream/imm32
- 406     # . . call
- 407     e8/call  check-next-stream-line-equal/disp32
- 408     # . . discard args
- 409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 410     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
- 411     # . . push args
- 412     68/push  "F - test-convert/5"/imm32
- 413     68/push  "10 11"/imm32
- 414     68/push  _test-output-stream/imm32
- 415     # . . call
- 416     e8/call  check-next-stream-line-equal/disp32
- 417     # . . discard args
- 418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 419     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
- 420     # . . push args
- 421     68/push  "F - test-convert/6"/imm32
- 422     68/push  "== data 0x0a000000"/imm32
- 423     68/push  _test-output-stream/imm32
- 424     # . . call
- 425     e8/call  check-next-stream-line-equal/disp32
- 426     # . . discard args
- 427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 428     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
- 429     # . . push args
- 430     68/push  "F - test-convert/7"/imm32
- 431     68/push  "4 5/imm32"/imm32
- 432     68/push  _test-output-stream/imm32
- 433     # . . call
- 434     e8/call  check-next-stream-line-equal/disp32
- 435     # . . discard args
- 436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 437     # . epilog
- 438     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 439     5d/pop-to-EBP
- 440     c3/return
- 441 
- 442 read-segments:  # in : (address buffered-file), table : (address stream row)
- 443     # pseudocode:
- 444     #   var curr-segment = null
- 445     #   var line = new-stream(512, 1)
- 446     #   while true
- 447     #     clear-stream(line)
- 448     #     read-line-buffered(in, line)
- 449     #     if (line->write == 0) break             # end of file
- 450     #     var word-slice = next-word(line)
- 451     #     if slice-empty?(word-slice)             # whitespace
- 452     #       continue
- 453     #     if slice-starts-with?(word-slice, "#")  # comment
- 454     #       continue
- 455     #     if slice-equal?(word-slice, "==")
- 456     #       var segment-name = next-word(line)
- 457     #       curr-segment = get-or-insert-segment(table, segment-name, Segment-size)
- 458     #       if curr-segment->write == 0
- 459     #         rewind-stream(line)
- 460     #         write-stream(curr-segment, line)
- 461     #     else
- 462     #       rewind-stream(line)
- 463     #       write-stream(curr-segment, line)  # abort if curr-segment overflows
- 464     #
- 465     # word-slice and segment-name are both slices with disjoint lifetimes, so
- 466     # we'll use the same address for them.
- 467     #
- 468     # registers:
- 469     #   line: ECX
- 470     #   word-slice and segment-name: EDX
- 471     #   segment-name and curr-segment: EBX
- 472     #   word-slice->start: ESI
- 473     #   temporary: EAX
- 474     #
- 475     # . prolog
- 476     55/push-EBP
- 477     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 478     # . save registers
- 479     50/push-EAX
- 480     51/push-ECX
- 481     52/push-EDX
- 482     53/push-EBX
- 483     56/push-ESI
- 484     # var line/ECX : (address stream byte) = stream(512)
- 485     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
- 486     68/push  0x200/imm32/length
- 487     68/push  0/imm32/read
- 488     68/push  0/imm32/write
- 489     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 490     # var word-slice/EDX = {0, 0}
- 491     68/push  0/imm32/end
- 492     68/push  0/imm32/curr
- 493     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
- 494 $read-segments:loop:
- 495     # clear-stream(line)
- 496     # . . push args
- 497     51/push-ECX
- 498     # . . call
- 499     e8/call  clear-stream/disp32
- 500     # . . discard args
- 501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 502     # read-line-buffered(in, line)
- 503     # . . push args
- 504     51/push-ECX
- 505     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 506     # . . call
- 507     e8/call  read-line-buffered/disp32
- 508     # . . discard args
- 509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 510 $read-segments:check0:
- 511     # if (line->write == 0) break
- 512     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
- 513     0f 84/jump-if-equal  $read-segments:break/disp32
- 514 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
- 540     # next-word(line, word-slice)
- 541     # . . push args
- 542     52/push-EDX
- 543     51/push-ECX
- 544     # . . call
- 545     e8/call  next-word/disp32
- 546     # . . discard args
- 547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 548 $read-segments:check1:
- 549     # if (slice-empty?(word-slice)) continue
- 550     # . EAX = slice-empty?(word-slice)
- 551     # . . push args
- 552     52/push-EDX
- 553     # . . call
- 554     e8/call  slice-empty?/disp32
- 555     # . . discard args
- 556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 557     # . if (EAX != 0) continue
- 558     3d/compare-EAX-and  0/imm32
- 559     0f 85/jump-if-not-equal  $read-segments:loop/disp32
- 560 $read-segments:check-for-comment:
- 561     # if (slice-starts-with?(word-slice, "#")) continue
- 562     # . start/ESI = word-slice->start
- 563     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *ECX to ESI
- 564     # . c/EAX = *start
- 565     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 566     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
- 567     # . if (EAX == '#') continue
- 568     3d/compare-EAX-and  0x23/imm32/hash
- 569     0f 84/jump-if-equal  $read-segments:loop/disp32
- 570 $read-segments:check-for-segment-header:
- 571 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
- 613     # if slice-equal?(word-slice, "==")
- 614     #   segment-name = next-word(line)
- 615     #   curr-segment = get-or-insert(table, segment-name)
- 616     #   if (curr-segment->write > 0) continue
- 617     # . EAX = slice-equal?(word-slice, "==")
- 618     # . . push args
- 619     68/push  "=="/imm32
- 620     52/push-EDX
- 621     # . . call
- 622     e8/call  slice-equal?/disp32
- 623     # . . discard args
- 624     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 625     # . if (EAX == 0) goto check3
- 626     3d/compare-EAX-and  0/imm32
- 627     0f 84/jump-if-equal  $read-segments:regular-line/disp32
- 628     # . next-word(line, segment-name)
- 629     # . . push args
- 630     52/push-EDX
- 631     51/push-ECX
- 632     # . . call
- 633     e8/call  next-word/disp32
- 634     # . . discard args
- 635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 636 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
- 678     # . EAX = get-or-insert-segment(table, segment-name, Segment-size)
- 679     # . . push args
- 680     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
- 681     52/push-EDX
- 682     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 683     # . . call
- 684     e8/call  get-or-insert-segment/disp32
- 685     # . . discard args
- 686     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 687     # . curr-segment = EAX
- 688     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
- 689     # . if (curr-segment->write > 0) continue
- 690     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
- 691     3d/compare-EAX-and  0/imm32
- 692     0f 8f/jump-if-greater  $read-segments:loop/disp32
- 693     # fall through
- 694 $read-segments:regular-line:
- 695     # rewind-stream(line)
- 696     # . . push args
- 697     51/push-ECX
- 698     # . . call
- 699     e8/call  rewind-stream/disp32
- 700     # . . discard args
- 701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 702     # write-stream(curr-segment, line)
- 703     # . . push args
- 704     51/push-ECX
- 705     53/push-EBX
- 706     # . . call
- 707     e8/call  write-stream/disp32
- 708     # . . discard args
- 709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 710     # loop
- 711     e9/jump  $read-segments:loop/disp32
- 712 $read-segments:break:
- 713 $read-segments:end:
- 714     # . reclaim locals
- 715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
- 716     # . restore registers
- 717     5e/pop-to-ESI
- 718     5b/pop-to-EBX
- 719     5a/pop-to-EDX
- 720     59/pop-to-ECX
- 721     58/pop-to-EAX
- 722     # . epilog
- 723     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 724     5d/pop-to-EBP
- 725     c3/return
- 726 
- 727 write-segments:  # out : (address buffered-file), table : (address stream row)
- 728     # pseudocode:
- 729     #   var curr = table->data
- 730     #   var max = table->data + table->write
- 731     #   while curr < max
- 732     #     stream = table[i].stream
- 733     #     write-stream-data(out, stream)
- 734     #     curr += 8
- 735     #   flush(out)
- 736     #
- 737     # . prolog
- 738     55/push-EBP
- 739     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 740     # . save registers
- 741     50/push-EAX
- 742     52/push-EDX
- 743     56/push-ESI
- 744     # ESI = table
- 745     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
- 746     # write/EDX = table->write
- 747     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
- 748     # curr/ESI = table->data
- 749     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to EAX
- 750     # max/EDX = curr + write
- 751     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # add ESI to EDX
- 752 $write-segments:loop:
- 753     # if (curr >= max) break
- 754     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
- 755     7d/jump-if-greater-or-equal  $write-segments:break/disp8
- 756     # stream/EAX = table[i].stream
- 757     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
- 758     # write-stream-data(out, stream)
- 759     # . . push args
- 760     50/push-EAX
- 761     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 762     # . . call
- 763     e8/call  write-stream-data/disp32
- 764     # . . discard args
- 765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 766 $write-segments:continue:
- 767     # curr += 8
- 768     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               8/imm32           # add to ESI
- 769     eb/jump  $write-segments:loop/disp8
- 770 $write-segments:break:
- 771     # flush(out)
- 772     # . . push args
- 773     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 774     # . . call
- 775     e8/call  flush/disp32
- 776     # . . discard args
- 777     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 778 $write-segments:end:
- 779     # . restore registers
- 780     5e/pop-to-ESI
- 781     5a/pop-to-EDX
- 782     58/pop-to-EAX
- 783     # . epilog
- 784     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 785     5d/pop-to-EBP
- 786     c3/return
- 787 
- 788 ## helpers
- 789 
- 790 # TODO: pass in an allocation descriptor
- 791 get-or-insert-segment:  # table : (address stream row), s : (address slice), n : int -> EAX : (address stream)
- 792     # pseudocode:
- 793     #   curr = table->data
- 794     #   max = &table->data[table->write]
- 795     #   while curr < max
- 796     #     if slice-equal?(s, *curr)
- 797     #       return *(curr+4)
- 798     #     curr += 8
- 799     #   if table->write < table->length
- 800     #     *max = slice-to-string(Heap, s)
- 801     #     result = new-stream(Heap, n, 1)
- 802     #     *(max+4) = result
- 803     #     table->write += 8
- 804     #     return result
- 805     #   return 0
- 806     #
- 807     # . prolog
- 808     55/push-EBP
- 809     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 810     # . save registers
- 811     51/push-ECX
- 812     52/push-EDX
- 813     56/push-ESI
- 814     # ESI = table
- 815     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
- 816     # curr/ECX = table->data
- 817     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
- 818     # max/EDX = table->data + table->write
- 819     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
- 820     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
- 821 $get-or-insert-segment:search-loop:
- 822     # if (curr >= max) break
- 823     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
- 824     7d/jump-if-greater-or-equal  $get-or-insert-segment:not-found/disp8
- 825     # if (slice-equal?(s, *curr)) return *(curr+4)
- 826     # . EAX = slice-equal?(s, *curr)
- 827     # . . push args
- 828     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
- 829     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 830     # . . call
- 831     e8/call  slice-equal?/disp32
- 832     # . . discard args
- 833     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 834     # . if (EAX != 0) return EAX = *(curr+4)
- 835     3d/compare-EAX-and  0/imm32
- 836     74/jump-if-equal  $get-or-insert-segment:mismatch/disp8
- 837     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
- 838     eb/jump  $get-or-insert-segment:end/disp8
- 839 $get-or-insert-segment:mismatch:
- 840     # curr += 8
- 841     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
- 842     # loop
- 843     eb/jump  $get-or-insert-segment:search-loop/disp8
- 844 $get-or-insert-segment:not-found:
- 845     # result/EAX = 0
- 846     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 847     # if (table->write >= table->length) abort
- 848     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
- 849     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
- 850     7d/jump-if-greater-or-equal  $get-or-insert-segment:abort/disp8
- 851     # *max = slice-to-string(Heap, s)
- 852     # . EAX = slice-to-string(Heap, s)
- 853     # . . push args
- 854     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 855     68/push  Heap/imm32
- 856     # . . call
- 857     e8/call  slice-to-string/disp32
- 858     # . . discard args
- 859     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 860     # . *max = EAX
- 861     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
- 862     # result/EAX = new-stream(Heap, n, 1)
- 863     # . . push args
- 864     68/push  1/imm32
- 865     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
- 866     68/push  Heap/imm32
- 867     # . . call
- 868     e8/call  new-stream/disp32
- 869     # . . discard args
- 870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 871     # *(max+4) = result
- 872     89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
- 873     # table->write += 8
- 874     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               8/imm32           # add to *ESI
- 875 $get-or-insert-segment:end:
- 876     # . restore registers
- 877     5e/pop-to-ESI
- 878     5a/pop-to-EDX
- 879     59/pop-to-ECX
- 880     # . epilog
- 881     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 882     5d/pop-to-EBP
- 883     c3/return
- 884 
- 885 $get-or-insert-segment:abort:
- 886     # . _write(2/stderr, error)
- 887     # . . push args
- 888     68/push  "get-or-insert-segment: too many segments\n"/imm32
- 889     68/push  2/imm32/stderr
- 890     # . . call
- 891     e8/call  _write/disp32
- 892     # . . discard args
- 893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 894     # . syscall(exit, 1)
- 895     bb/copy-to-EBX  1/imm32
- 896     b8/copy-to-EAX  1/imm32/exit
- 897     cd/syscall  0x80/imm8
- 898     # never gets here
- 899 
- 900 test-get-or-insert-segment:
- 901     # . prolog
- 902     55/push-EBP
- 903     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 904     # var table/ECX : (address stream byte) = stream(2 * 8)
- 905     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
- 906     68/push  0x10/imm32/length
- 907     68/push  0/imm32/read
- 908     68/push  0/imm32/write
- 909     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 910     # EDX : (address slice) = "code"
- 911     68/push  _test-code-segment-end/imm32/end
- 912     68/push  _test-code-segment/imm32/start
- 913     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
- 914 $test-get-or-insert-segment:first-call:
- 915     # - start with an empty table, insert one segment, verify that it was inserted
- 916     # segment/EAX = get-or-insert-segment(table, "code" slice, 10)
- 917     # . . push args
- 918     68/push  0xa/imm32/segment-length
- 919     52/push-EDX
- 920     51/push-ECX
- 921     # . . call
- 922     e8/call  get-or-insert-segment/disp32
- 923     # . . discard args
- 924     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 925     # save segment
- 926     50/push-EAX
- 927     # if (segment != 0) goto next check
- 928     3d/compare-EAX-and  0/imm32
- 929     75/jump-if-not-equal  $test-get-or-insert-segment:check1/disp8
- 930     # fail test
- 931     # . _write(2/stderr, msg)
- 932     # . . push args
- 933     68/push  "F - test-get-or-insert-segment/0\n"/imm32
- 934     68/push  2/imm32/stderr
- 935     # . . call
- 936     e8/call  _write/disp32
- 937     # . . discard args
- 938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 939     # . increment Num-test-failures
- 940     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Num-test-failures/disp32          # increment *Num-test-failures
- 941     e9/jump  $test-get-or-insert-segment:end/disp32
- 942 $test-get-or-insert-segment:check1:
- 943     # check-ints-equal(segment->length, 10, msg)
- 944     # . . push args
- 945     68/push  "F - test-get-or-insert-segment/1"/imm32
- 946     68/push  0xa/imm32/segment-length
- 947     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
- 948     # . . call
- 949     e8/call  check-ints-equal/disp32
- 950     # . . discard args
- 951     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 952 $test-get-or-insert-segment:check2:
- 953     # check-ints-equal(table->write, rowsize = 8, msg)
- 954     # . . push args
- 955     68/push  "F - test-get-or-insert-segment/2"/imm32
- 956     68/push  8/imm32/row-size
- 957     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
- 958     # . . call
- 959     e8/call  check-ints-equal/disp32
- 960     # . . discard args
- 961     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 962     # EAX = string-equal?(*table->data, "code")
- 963     # . . push args
- 964     68/push  "code"/imm32
- 965     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
- 966     # . . call
- 967     e8/call  string-equal?/disp32
- 968     # . . discard args
- 969     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 970     # check-ints-equal(EAX, 1, msg)
- 971     # . . push args
- 972     68/push  "F - test-get-or-insert-segment/3"/imm32
- 973     68/push  1/imm32
- 974     50/push-EAX
- 975     # . . call
- 976     e8/call  check-ints-equal/disp32
- 977     # . . discard args
- 978     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 979 $test-get-or-insert-segment:check3:
- 980     # stream/EAX = *(table->data+4)
- 981     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(ECX+16) to EAX
- 982     # check-ints-equal(stream->length, 10, msg)
- 983     # . . push args
- 984     68/push  "F - test-get-or-insert-segment/4"/imm32
- 985     68/push  0xa/imm32/segment-size
- 986     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
- 987     # . . call
- 988     e8/call  check-ints-equal/disp32
- 989     # . . discard args
- 990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 991 $test-get-or-insert-segment:second-call:
- 992     # - insert the same segment name again, verify that it was reused
- 993     # segment2/EAX = get-or-insert-segment(table, "code" slice, 8)
- 994     # . . push args
- 995     68/push  8/imm32/segment-length
- 996     52/push-EDX
- 997     51/push-ECX
- 998     # . . call
- 999     e8/call  get-or-insert-segment/disp32
-1000     # . . discard args
-1001     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1002     # restore old segment1
-1003     5a/pop-to-EDX
-1004     # check-ints-equal(segment2/EAX, segment1/EDX, msg)
-1005     # . . push args
-1006     68/push  "F - test-get-or-insert-segment/5"/imm32
-1007     52/push-EDX
-1008     50/push-EAX
-1009     # . . call
-1010     e8/call  check-ints-equal/disp32
-1011     # . . discard args
-1012     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1013     # no change to table size
-1014     # . check-ints-equal(table->write, rowsize = 8, msg)
-1015     # . . push args
-1016     68/push  "F - test-get-or-insert-segment/6"/imm32
-1017     68/push  8/imm32/row-size
-1018     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
-1019     # . . call
-1020     e8/call  check-ints-equal/disp32
-1021     # . . discard args
-1022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1023 $test-get-or-insert-segment:third-call:
-1024     # - insert a new segment name, verify that it was inserted
-1025     # EDX : (address slice) = "data"
-1026     c7          0/subop/copy        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               _test-data-segment/imm32  # copy to *EDX
-1027     c7          0/subop/copy        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         _test-data-segment-end/imm32  # copy to *(EDX+4)
-1028     # segment2/EAX = get-or-insert-segment(table, "data" slice, 8)
-1029     # . . push args
-1030     68/push  8/imm32/segment-length
-1031     52/push-EDX
-1032     51/push-ECX
-1033     # . . call
-1034     e8/call  get-or-insert-segment/disp32
-1035     # . . discard args
-1036     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1037     # table gets a new row
-1038     # . check-ints-equal(table->write, 2 rows = 16, msg)
-1039     # . . push args
-1040     68/push  "F - test-get-or-insert-segment/7"/imm32
-1041     68/push  0x10/imm32/two-rows
-1042     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
-1043     # . . call
-1044     e8/call  check-ints-equal/disp32
-1045     # . . discard args
-1046     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1047 $test-get-or-insert-segment:end:
-1048     # . epilog
-1049     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1050     5d/pop-to-EBP
-1051     c3/return
-1052 
-1053 # (re)compute the bounds of the next word in the line
-1054 # return empty string on reaching end of file
-1055 next-word:  # line : (address stream byte), out : (address slice)
-1056     # . prolog
-1057     55/push-EBP
-1058     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1059     # . save registers
-1060     50/push-EAX
-1061     51/push-ECX
-1062     56/push-ESI
-1063     57/push-EDI
-1064     # ESI = line
-1065     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-1066     # EDI = out
-1067     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
-1068     # skip-chars-matching(line, ' ')
-1069     # . . push args
-1070     68/push  0x20/imm32/space
-1071     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1072     # . . call
-1073     e8/call  skip-chars-matching/disp32
-1074     # . . discard args
-1075     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1076 $next-word:check0:
-1077     # if (line->read >= line->write) clear out and return
-1078     # . EAX = line->read
-1079     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
-1080     # . if (EAX < line->write) goto next check
-1081     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
-1082     7c/jump-if-lesser  $next-word:check-for-comment/disp8
-1083     # . return out = {0, 0}
-1084     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
-1085     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
-1086     eb/jump  $next-word:end/disp8
-1087 $next-word:check-for-comment:
-1088     # out->start = &line->data[line->read]
-1089     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-1090     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
-1091     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
-1092     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
-1093     # . EAX = line->data[line->read]
-1094     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-1095     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
-1096     # . compare
-1097     3d/compare-EAX-and  0x23/imm32/pound
-1098     75/jump-if-not-equal  $next-word:regular-word/disp8
-1099 $next-word:comment:
-1100     # . out->end = &line->data[line->write]
-1101     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-1102     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
-1103     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-1104     # . line->read = line->write
-1105     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
-1106     # . return
-1107     eb/jump  $next-word:end/disp8
-1108 $next-word:regular-word:
-1109     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
-1110     # . . push args
-1111     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1112     # . . call
-1113     e8/call  skip-chars-not-matching-whitespace/disp32
-1114     # . . discard args
-1115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1116     # out->end = &line->data[line->read]
-1117     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-1118     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
-1119     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-1120 $next-word:end:
-1121     # . restore registers
-1122     5f/pop-to-EDI
-1123     5e/pop-to-ESI
-1124     59/pop-to-ECX
-1125     58/pop-to-EAX
-1126     # . epilog
-1127     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1128     5d/pop-to-EBP
-1129     c3/return
-1130 
-1131 test-next-word:
-1132     # . prolog
-1133     55/push-EBP
-1134     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1135     # setup
-1136     # . clear-stream(_test-stream)
-1137     # . . push args
-1138     68/push  _test-stream/imm32
-1139     # . . call
-1140     e8/call  clear-stream/disp32
-1141     # . . discard args
-1142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1143     # var slice/ECX = {0, 0}
-1144     68/push  0/imm32/end
-1145     68/push  0/imm32/start
-1146     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1147     # write(_test-stream, "  ab")
-1148     # . . push args
-1149     68/push  "  ab"/imm32
-1150     68/push  _test-stream/imm32
-1151     # . . call
-1152     e8/call  write/disp32
-1153     # . . discard args
-1154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1155     # next-word(_test-stream, slice)
-1156     # . . push args
-1157     51/push-ECX
-1158     68/push  _test-stream/imm32
-1159     # . . call
-1160     e8/call  next-word/disp32
-1161     # . . discard args
-1162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1163     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
-1164     # . check-ints-equal(slice->start - _test-stream, 14, msg)
-1165     # . . push args
-1166     68/push  "F - test-next-word: start"/imm32
-1167     68/push  0xe/imm32
-1168     # . . push slice->start - _test-stream
-1169     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-1170     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-1171     50/push-EAX
-1172     # . . call
-1173     e8/call  check-ints-equal/disp32
-1174     # . . discard args
-1175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1176     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
-1177     # . check-ints-equal(slice->end - _test-stream, 16, msg)
-1178     # . . push args
-1179     68/push  "F - test-next-word: end"/imm32
-1180     68/push  0x10/imm32
-1181     # . . push slice->end - _test-stream
-1182     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-1183     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-1184     50/push-EAX
-1185     # . . call
-1186     e8/call  check-ints-equal/disp32
-1187     # . . discard args
-1188     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1189     # . epilog
-1190     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1191     5d/pop-to-EBP
-1192     c3/return
-1193 
-1194 test-next-word-returns-whole-comment:
-1195     # . prolog
-1196     55/push-EBP
-1197     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1198     # setup
-1199     # . clear-stream(_test-stream)
-1200     # . . push args
-1201     68/push  _test-stream/imm32
-1202     # . . call
-1203     e8/call  clear-stream/disp32
-1204     # . . discard args
-1205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1206     # var slice/ECX = {0, 0}
-1207     68/push  0/imm32/end
-1208     68/push  0/imm32/start
-1209     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1210     # write(_test-stream, "  # a")
-1211     # . . push args
-1212     68/push  "  # a"/imm32
-1213     68/push  _test-stream/imm32
-1214     # . . call
-1215     e8/call  write/disp32
-1216     # . . discard args
-1217     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1218     # next-word(_test-stream, slice)
-1219     # . . push args
-1220     51/push-ECX
-1221     68/push  _test-stream/imm32
-1222     # . . call
-1223     e8/call  next-word/disp32
-1224     # . . discard args
-1225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1226     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
-1227     # . check-ints-equal(slice->start - _test-stream, 14, msg)
-1228     # . . push args
-1229     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
-1230     68/push  0xe/imm32
-1231     # . . push slice->start - _test-stream
-1232     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-1233     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-1234     50/push-EAX
-1235     # . . call
-1236     e8/call  check-ints-equal/disp32
-1237     # . . discard args
-1238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1239     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
-1240     # . check-ints-equal(slice->end - _test-stream, 17, msg)
-1241     # . . push args
-1242     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
-1243     68/push  0x11/imm32
-1244     # . . push slice->end - _test-stream
-1245     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-1246     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-1247     50/push-EAX
-1248     # . . call
-1249     e8/call  check-ints-equal/disp32
-1250     # . . discard args
-1251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1252     # . epilog
-1253     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1254     5d/pop-to-EBP
-1255     c3/return
-1256 
-1257 test-next-word-returns-empty-string-on-eof:
-1258     # . prolog
-1259     55/push-EBP
-1260     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1261     # setup
-1262     # . clear-stream(_test-stream)
-1263     # . . push args
-1264     68/push  _test-stream/imm32
-1265     # . . call
-1266     e8/call  clear-stream/disp32
-1267     # . . discard args
-1268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1269     # var slice/ECX = {0, 0}
-1270     68/push  0/imm32/end
-1271     68/push  0/imm32/start
-1272     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1273     # write nothing to _test-stream
-1274     # next-word(_test-stream, slice)
-1275     # . . push args
-1276     51/push-ECX
-1277     68/push  _test-stream/imm32
-1278     # . . call
-1279     e8/call  next-word/disp32
-1280     # . . discard args
-1281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1282     # check-ints-equal(slice->end - slice->start, 0, msg)
-1283     # . . push args
-1284     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
-1285     68/push  0/imm32
-1286     # . . push slice->end - slice->start
-1287     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-1288     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
-1289     50/push-EAX
-1290     # . . call
-1291     e8/call  check-ints-equal/disp32
-1292     # . . discard args
-1293     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1294     # . epilog
-1295     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1296     5d/pop-to-EBP
-1297     c3/return
-1298 
-1299 == data
-1300 
-1301 _test-code-segment:
-1302   63/c 6f/o 64/d 65/e
-1303 _test-code-segment-end:
-1304 
-1305 _test-data-segment:
-1306   64/d 61/a 74/t 61/a
-1307 _test-data-segment-end:
-1308 
-1309 Segment-size:
-1310   0x1000/imm32/4KB
-1311 
-1312 Heap:
-1313   # curr
-1314   0/imm32
-1315   # limit
-1316   0/imm32
-1317 
-1318 # . . vim:nowrap:textwidth=0
+  1 # Read a series of segments from stdin and concatenate segments with the same
+  2 # name on stdout.
+  3 #
+  4 # Segments are emitted in order of first encounter.
+  5 #
+  6 # Drop lines that are all comments. They could get misleading after assortment
+  7 # because we don't know if they refer to the line above or the line below.
+  8 #
+  9 # To run (from the subx/ directory):
+ 10 #   $ ./subx translate *.subx apps/assort.subx -o apps/assort
+ 11 #   $ cat x
+ 12 #   == code
+ 13 #   abc
+ 14 #   == code
+ 15 #   def
+ 16 #   $ cat x  |./subx run apps/assort
+ 17 #   == code
+ 18 #   abc
+ 19 #   def
+ 20 
+ 21 == code
+ 22 #   instruction                     effective address                                                   register    displacement    immediate
+ 23 # . op          subop               mod             rm32          base        index         scale       r32
+ 24 # . 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
+ 25 
+ 26 Entry:
+ 27     # initialize heap
+ 28     # . Heap = new-segment(64KB)
+ 29     # . . push args
+ 30     68/push  Heap/imm32
+ 31     68/push  0x10000/imm32/64KB
+ 32     # . . call
+ 33     e8/call  new-segment/disp32
+ 34     # . . discard args
+ 35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 36 
+ 37     # run tests if necessary, convert stdin if not
+ 38     # . prolog
+ 39     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 40     # - if argc > 1 and argv[1] == "test", then return run_tests()
+ 41     # . argc > 1
+ 42     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
+ 43     7e/jump-if-lesser-or-equal  $run-main/disp8
+ 44     # . argv[1] == "test"
+ 45     # . . push args
+ 46     68/push  "test"/imm32
+ 47     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 48     # . . call
+ 49     e8/call  kernel-string-equal?/disp32
+ 50     # . . discard args
+ 51     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 52     # . check result
+ 53     3d/compare-EAX-and  1/imm32
+ 54     75/jump-if-not-equal  $run-main/disp8
+ 55     # . run-tests()
+ 56     e8/call  run-tests/disp32
+ 57     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 58     eb/jump  $main:end/disp8
+ 59 $run-main:
+ 60     # - otherwise convert stdin
+ 61     # var ed/EAX : exit-descriptor
+ 62     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 63     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
+ 64     # configure ed to really exit()
+ 65     # . ed->target = 0
+ 66     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+ 67     # return convert(Stdin, 1/stdout, 2/stderr, ed)
+ 68     # . . push args
+ 69     50/push-EAX/ed
+ 70     68/push  Stderr/imm32
+ 71     68/push  Stdout/imm32
+ 72     68/push  Stdin/imm32
+ 73     # . . call
+ 74     e8/call  convert/disp32
+ 75     # . . discard args
+ 76     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+ 77     # . syscall(exit, 0)
+ 78     bb/copy-to-EBX  0/imm32
+ 79 $main:end:
+ 80     b8/copy-to-EAX  1/imm32/exit
+ 81     cd/syscall  0x80/imm8
+ 82 
+ 83 # data structure:
+ 84 #   table: (address stream {string, (address stream byte)})     (8 bytes per row)
+ 85 # inefficient; uses sequential search for looking up segments by name
+ 86 
+ 87 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+ 88     # pseudocode:
+ 89     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
+ 90     #   read-segments(in, table)
+ 91     #   write-segments(out, table)
+ 92     #
+ 93     # . prolog
+ 94     55/push-EBP
+ 95     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 96     # . save registers
+ 97     51/push-ECX
+ 98     # var table/ECX : (address stream byte) = stream(10 * 8)
+ 99     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x50/imm32        # subtract from ESP
+100     68/push  0x50/imm32/length
+101     68/push  0/imm32/read
+102     68/push  0/imm32/write
+103     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+104     # clear-stream(table)
+105     # . . push args
+106     51/push-ECX
+107     # . . call
+108     e8/call  clear-stream/disp32
+109     # . . discard args
+110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+111 $convert:read:
+112     # read-segments(in, table)
+113     # . . push args
+114     51/push-ECX
+115     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+116     # . . call
+117     e8/call  read-segments/disp32
+118     # . . discard args
+119     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+120 $convert:write:
+121     # write-segments(out, table)
+122     # . . push args
+123     51/push-ECX
+124     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+125     # . . call
+126     e8/call  write-segments/disp32
+127     # . . discard args
+128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+129 $convert:end:
+130     # . reclaim locals
+131     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
+132     # . restore registers
+133     59/pop-to-ECX
+134     # . epilog
+135     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+136     5d/pop-to-EBP
+137     c3/return
+138 
+139 test-convert:
+140     # . prolog
+141     55/push-EBP
+142     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+143     # setup
+144     # . clear-stream(_test-input-stream)
+145     # . . push args
+146     68/push  _test-input-stream/imm32
+147     # . . call
+148     e8/call  clear-stream/disp32
+149     # . . discard args
+150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+151     # . clear-stream(_test-input-buffered-file+4)
+152     # . . push args
+153     b8/copy-to-EAX  _test-input-buffered-file/imm32
+154     05/add-to-EAX  4/imm32
+155     50/push-EAX
+156     # . . call
+157     e8/call  clear-stream/disp32
+158     # . . discard args
+159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+160     # . clear-stream(_test-output-stream)
+161     # . . push args
+162     68/push  _test-output-stream/imm32
+163     # . . call
+164     e8/call  clear-stream/disp32
+165     # . . discard args
+166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+167     # . clear-stream(_test-output-buffered-file+4)
+168     # . . push args
+169     b8/copy-to-EAX  _test-output-buffered-file/imm32
+170     05/add-to-EAX  4/imm32
+171     50/push-EAX
+172     # . . call
+173     e8/call  clear-stream/disp32
+174     # . . discard args
+175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+176     # initialize input (meta comments in parens)
+177     #   # comment 1
+178     #     # comment 2 indented
+179     #   == code 0x09000000  (new segment)
+180     #   # comment 3 inside a segment
+181     #   1
+182     #                         (empty line)
+183     #   2 3 # comment 4 inline with other contents
+184     #   == data 0x0a000000  (new segment)
+185     #   4 5/imm32
+186     #   == code  (existing segment but non-contiguous with previous iteration)
+187     #   6 7
+188     #   8 9  (multiple lines)
+189     #   == code  (existing segment contiguous with previous iteration)
+190     #   10 11
+191     # . write(_test-input-stream, "# comment 1\n")
+192     # . . push args
+193     68/push  "# comment 1\n"/imm32
+194     68/push  _test-input-stream/imm32
+195     # . . call
+196     e8/call  write/disp32
+197     # . . discard args
+198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+199     # . write(_test-input-stream, "  # comment 2 indented\n")
+200     # . . push args
+201     68/push  "  # comment 2 indented\n"/imm32
+202     68/push  _test-input-stream/imm32
+203     # . . call
+204     e8/call  write/disp32
+205     # . . discard args
+206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+207     # . write(_test-input-stream, "== code 0x09000000\n")
+208     # . . push args
+209     68/push  "== code 0x09000000\n"/imm32
+210     68/push  _test-input-stream/imm32
+211     # . . call
+212     e8/call  write/disp32
+213     # . . discard args
+214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+215     # . write(_test-input-stream, "# comment 3 inside a segment\n")
+216     # . . push args
+217     68/push  "# comment 3 inside a segment\n"/imm32
+218     68/push  _test-input-stream/imm32
+219     # . . call
+220     e8/call  write/disp32
+221     # . . discard args
+222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+223     # . write(_test-input-stream, "1\n")
+224     # . . push args
+225     68/push  "1\n"/imm32
+226     68/push  _test-input-stream/imm32
+227     # . . call
+228     e8/call  write/disp32
+229     # . . discard args
+230     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+231     # . write(_test-input-stream, "\n")  # empty line
+232     # . . push args
+233     68/push  "\n"/imm32
+234     68/push  _test-input-stream/imm32
+235     # . . call
+236     e8/call  write/disp32
+237     # . . discard args
+238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+239     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
+240     # . . push args
+241     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
+242     68/push  _test-input-stream/imm32
+243     # . . call
+244     e8/call  write/disp32
+245     # . . discard args
+246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+247     # . write(_test-input-stream, "== data 0x0a000000\n")
+248     # . . push args
+249     68/push  "== data 0x0a000000\n"/imm32
+250     68/push  _test-input-stream/imm32
+251     # . . call
+252     e8/call  write/disp32
+253     # . . discard args
+254     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+255     # . write(_test-input-stream, "4 5/imm32\n")
+256     # . . push args
+257     68/push  "4 5/imm32\n"/imm32
+258     68/push  _test-input-stream/imm32
+259     # . . call
+260     e8/call  write/disp32
+261     # . . discard args
+262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+263     # . write(_test-input-stream, "== code\n")
+264     # . . push args
+265     68/push  "== code\n"/imm32
+266     68/push  _test-input-stream/imm32
+267     # . . call
+268     e8/call  write/disp32
+269     # . . discard args
+270     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+271     # . write(_test-input-stream, "6 7\n")
+272     # . . push args
+273     68/push  "6 7\n"/imm32
+274     68/push  _test-input-stream/imm32
+275     # . . call
+276     e8/call  write/disp32
+277     # . . discard args
+278     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+279     # . write(_test-input-stream, "8 9\n")
+280     # . . push args
+281     68/push  "8 9\n"/imm32
+282     68/push  _test-input-stream/imm32
+283     # . . call
+284     e8/call  write/disp32
+285     # . . discard args
+286     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+287     # . write(_test-input-stream, "== code\n")
+288     # . . push args
+289     68/push  "== code\n"/imm32
+290     68/push  _test-input-stream/imm32
+291     # . . call
+292     e8/call  write/disp32
+293     # . . discard args
+294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+295     # . write(_test-input-stream, "10 11\n")
+296     # . . push args
+297     68/push  "10 11\n"/imm32
+298     68/push  _test-input-stream/imm32
+299     # . . call
+300     e8/call  write/disp32
+301     # . . discard args
+302     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+303     # convert(_test-input-buffered-file, _test-output-buffered-file)
+304     # . . push args
+305     68/push  _test-output-buffered-file/imm32
+306     68/push  _test-input-buffered-file/imm32
+307     # . . call
+308     e8/call  convert/disp32
+309     # . . discard args
+310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+311     # . flush(_test-output-buffered-file)
+312     # . . push args
+313     68/push  _test-output-buffered-file/imm32
+314     # . . call
+315     e8/call  flush/disp32
+316     # . . discard args
+317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+318     # check output
+319     #   == code 0x09000000
+320     #   1
+321     #   2 3 # comment 4 inline with other contents
+322     #   6 7
+323     #   8 9
+324     #   10 11
+325     #   == data 0x0a000000
+326     #   4 5/imm32
+327 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+360     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
+361     # . . push args
+362     68/push  "F - test-convert/0"/imm32
+363     68/push  "== code 0x09000000"/imm32
+364     68/push  _test-output-stream/imm32
+365     # . . call
+366     e8/call  check-next-stream-line-equal/disp32
+367     # . . discard args
+368     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+369     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
+370     # . . push args
+371     68/push  "F - test-convert/1"/imm32
+372     68/push  "1"/imm32
+373     68/push  _test-output-stream/imm32
+374     # . . call
+375     e8/call  check-next-stream-line-equal/disp32
+376     # . . discard args
+377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+378     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
+379     # . . push args
+380     68/push  "F - test-convert/2"/imm32
+381     68/push  "2 3 # comment 4 inline with other contents"/imm32
+382     68/push  _test-output-stream/imm32
+383     # . . call
+384     e8/call  check-next-stream-line-equal/disp32
+385     # . . discard args
+386     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+387     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
+388     # . . push args
+389     68/push  "F - test-convert/3"/imm32
+390     68/push  "6 7"/imm32
+391     68/push  _test-output-stream/imm32
+392     # . . call
+393     e8/call  check-next-stream-line-equal/disp32
+394     # . . discard args
+395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+396     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
+397     # . . push args
+398     68/push  "F - test-convert/4"/imm32
+399     68/push  "8 9"/imm32
+400     68/push  _test-output-stream/imm32
+401     # . . call
+402     e8/call  check-next-stream-line-equal/disp32
+403     # . . discard args
+404     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+405     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
+406     # . . push args
+407     68/push  "F - test-convert/5"/imm32
+408     68/push  "10 11"/imm32
+409     68/push  _test-output-stream/imm32
+410     # . . call
+411     e8/call  check-next-stream-line-equal/disp32
+412     # . . discard args
+413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+414     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
+415     # . . push args
+416     68/push  "F - test-convert/6"/imm32
+417     68/push  "== data 0x0a000000"/imm32
+418     68/push  _test-output-stream/imm32
+419     # . . call
+420     e8/call  check-next-stream-line-equal/disp32
+421     # . . discard args
+422     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+423     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
+424     # . . push args
+425     68/push  "F - test-convert/7"/imm32
+426     68/push  "4 5/imm32"/imm32
+427     68/push  _test-output-stream/imm32
+428     # . . call
+429     e8/call  check-next-stream-line-equal/disp32
+430     # . . discard args
+431     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+432     # . epilog
+433     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+434     5d/pop-to-EBP
+435     c3/return
+436 
+437 # beware: leaks memory (one name per segment read)
+438 read-segments:  # in : (address buffered-file), table : (address stream {string, (address stream byte)})
+439     # pseudocode:
+440     #   var curr-segment = null
+441     #   var line = new-stream(512, 1)
+442     #   while true
+443     #     clear-stream(line)
+444     #     read-line-buffered(in, line)
+445     #     if (line->write == 0) break             # end of file
+446     #     var word-slice = next-word(line)
+447     #     if slice-empty?(word-slice)             # whitespace
+448     #       continue
+449     #     if slice-starts-with?(word-slice, "#")  # comment
+450     #       continue
+451     #     if slice-equal?(word-slice, "==")
+452     #       var segment-name = next-word(line)
+453     #       segment-slot = leaky-get-or-insert-slice(table, segment-name, row-size=8)
+454     #       curr-segment = *segment-slot
+455     #       if curr-segment != 0
+456     #         continue
+457     #       curr-segment = new-stream(Segment-size)
+458     #       *segment-slot = curr-segment
+459     #     rewind-stream(line)
+460     #     write-stream(curr-segment, line)  # abort if curr-segment overflows
+461     #
+462     # word-slice and segment-name are both slices with disjoint lifetimes, so
+463     # we'll use the same address for them.
+464     #
+465     # registers:
+466     #   line: ECX
+467     #   word-slice and segment-name: EDX
+468     #   segment-name and curr-segment: EBX
+469     #   word-slice->start: ESI
+470     #   temporary: EAX
+471     #
+472     # . prolog
+473     55/push-EBP
+474     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+475     # . save registers
+476     50/push-EAX
+477     51/push-ECX
+478     52/push-EDX
+479     53/push-EBX
+480     56/push-ESI
+481     # var line/ECX : (address stream byte) = stream(512)
+482     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
+483     68/push  0x200/imm32/length
+484     68/push  0/imm32/read
+485     68/push  0/imm32/write
+486     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+487     # var word-slice/EDX = {0, 0}
+488     68/push  0/imm32/end
+489     68/push  0/imm32/start
+490     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+491 $read-segments:loop:
+492     # clear-stream(line)
+493     # . . push args
+494     51/push-ECX
+495     # . . call
+496     e8/call  clear-stream/disp32
+497     # . . discard args
+498     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+499     # read-line-buffered(in, line)
+500     # . . push args
+501     51/push-ECX
+502     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+503     # . . call
+504     e8/call  read-line-buffered/disp32
+505     # . . discard args
+506     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+507 $read-segments:check0:
+508     # if (line->write == 0) break
+509     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
+510     0f 84/jump-if-equal  $read-segments:break/disp32
+511 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+537     # next-word(line, word-slice)
+538     # . . push args
+539     52/push-EDX
+540     51/push-ECX
+541     # . . call
+542     e8/call  next-word/disp32
+543     # . . discard args
+544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+545 $read-segments:check1:
+546     # if (slice-empty?(word-slice)) continue
+547     # . EAX = slice-empty?(word-slice)
+548     # . . push args
+549     52/push-EDX
+550     # . . call
+551     e8/call  slice-empty?/disp32
+552     # . . discard args
+553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+554     # . if (EAX != 0) continue
+555     3d/compare-EAX-and  0/imm32
+556     0f 85/jump-if-not-equal  $read-segments:loop/disp32
+557 $read-segments:check-for-comment:
+558     # if (slice-starts-with?(word-slice, "#")) continue
+559     # . start/ESI = word-slice->start
+560     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *ECX to ESI
+561     # . c/EAX = *start
+562     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+563     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
+564     # . if (EAX == '#') continue
+565     3d/compare-EAX-and  0x23/imm32/hash
+566     0f 84/jump-if-equal  $read-segments:loop/disp32
+567 $read-segments:check-for-segment-header:
+568 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+610     # if !slice-equal?(word-slice, "==") goto next check
+611     # . EAX = slice-equal?(word-slice, "==")
+612     # . . push args
+613     68/push  "=="/imm32
+614     52/push-EDX
+615     # . . call
+616     e8/call  slice-equal?/disp32
+617     # . . discard args
+618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+619     # . if (EAX == 0) goto check3
+620     3d/compare-EAX-and  0/imm32
+621     0f 84/jump-if-equal  $read-segments:regular-line/disp32
+622     # segment-name = next-word(line)
+623     # . . push args
+624     52/push-EDX
+625     51/push-ECX
+626     # . . call
+627     e8/call  next-word/disp32
+628     # . . discard args
+629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+630 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
+672     # segment-slot/EAX = leaky-get-or-insert-slice(table, segment-name, row-size=8)
+673     # . . push args
+674     68/push  8/imm32/row-size
+675     52/push-EDX
+676     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+677     # . . call
+678     e8/call  leaky-get-or-insert-slice/disp32
+679     # . . discard args
+680     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+681     # curr-segment = *segment-slot
+682     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy *EAX to EBX
+683     # if (curr-segment != 0) continue
+684     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
+685     0f 85/jump-if-not-equal  $read-segments:loop/disp32
+686     # curr-segment = new-stream(Heap, Segment-size, 1)
+687     # . save segment-slot
+688     50/push-EAX
+689     # . EAX = new-stream(Heap, Segment-size, 1)
+690     # . . push args
+691     68/push  1/imm32
+692     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
+693     68/push  Heap/imm32
+694     # . . call
+695     e8/call  new-stream/disp32
+696     # . . discard args
+697     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+698     # . curr-segment = EAX
+699     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+700     # . restore segment-slot
+701     58/pop-to-EAX
+702     # *segment-slot = curr-segment
+703     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to *EAX
+704     # fall through
+705 $read-segments:regular-line:
+706     # rewind-stream(line)
+707     # . . push args
+708     51/push-ECX
+709     # . . call
+710     e8/call  rewind-stream/disp32
+711     # . . discard args
+712     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+713     # write-stream(curr-segment, line)
+714     # . . push args
+715     51/push-ECX
+716     53/push-EBX
+717     # . . call
+718     e8/call  write-stream/disp32
+719     # . . discard args
+720     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+721     # loop
+722     e9/jump  $read-segments:loop/disp32
+723 $read-segments:break:
+724 $read-segments:end:
+725     # . reclaim locals
+726     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
+727     # . restore registers
+728     5e/pop-to-ESI
+729     5b/pop-to-EBX
+730     5a/pop-to-EDX
+731     59/pop-to-ECX
+732     58/pop-to-EAX
+733     # . epilog
+734     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+735     5d/pop-to-EBP
+736     c3/return
+737 
+738 write-segments:  # out : (address buffered-file), table : (address stream {string, (address stream byte)})
+739     # pseudocode:
+740     #   var curr = table->data
+741     #   var max = table->data + table->write
+742     #   while curr < max
+743     #     stream = table[i].stream
+744     #     write-stream-data(out, stream)
+745     #     curr += 8
+746     #   flush(out)
+747     #
+748     # . prolog
+749     55/push-EBP
+750     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+751     # . save registers
+752     50/push-EAX
+753     52/push-EDX
+754     56/push-ESI
+755     # ESI = table
+756     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+757     # write/EDX = table->write
+758     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+759     # curr/ESI = table->data
+760     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to EAX
+761     # max/EDX = curr + write
+762     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # add ESI to EDX
+763 $write-segments:loop:
+764     # if (curr >= max) break
+765     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
+766     73/jump-if-greater-or-equal-unsigned  $write-segments:break/disp8
+767     # stream/EAX = table[i].stream
+768     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+769     # write-stream-data(out, stream)
+770     # . . push args
+771     50/push-EAX
+772     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+773     # . . call
+774     e8/call  write-stream-data/disp32
+775     # . . discard args
+776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+777 $write-segments:continue:
+778     # curr += 8
+779     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               8/imm32           # add to ESI
+780     eb/jump  $write-segments:loop/disp8
+781 $write-segments:break:
+782     # flush(out)
+783     # . . push args
+784     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+785     # . . call
+786     e8/call  flush/disp32
+787     # . . discard args
+788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+789 $write-segments:end:
+790     # . restore registers
+791     5e/pop-to-ESI
+792     5a/pop-to-EDX
+793     58/pop-to-EAX
+794     # . epilog
+795     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+796     5d/pop-to-EBP
+797     c3/return
+798 
+799 == data
+800 
+801 Segment-size:
+802   0x1000/imm32/4KB
+803 
+804 # . . vim:nowrap:textwidth=0
 
diff --git a/html/subx/apps/crenshaw2-1.subx.html b/html/subx/apps/crenshaw2-1.subx.html index bbae9106..c7f3c335 100644 --- a/html/subx/apps/crenshaw2-1.subx.html +++ b/html/subx/apps/crenshaw2-1.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/crenshaw2-1.subx - - + + @@ -14,18 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxMinorFunction { color: #875f5f; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -42,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -94,553 +93,558 @@ if ('onhashchange' in window) { 31 # . 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 32 33 Entry: # run tests if necessary, call 'compile' if not - 34 - 35 #? # for debugging: run a single test; don't bother setting status code - 36 #? e8/call test-get-num-aborts-on-non-digit-in-Look/disp32 - 37 #? eb/jump $main:end/disp8 - 38 - 39 # . prolog - 40 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 41 # - if argc > 1 and argv[1] == "test", then return run_tests() - 42 # . argc > 1 - 43 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 44 7e/jump-if-lesser-or-equal $run-main/disp8 - 45 # . argv[1] == "test" - 46 # . . push args - 47 68/push "test"/imm32 - 48 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 49 # . . call - 50 e8/call kernel-string-equal?/disp32 - 51 # . . discard args - 52 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 53 # . check result - 54 3d/compare-EAX-and 1/imm32 - 55 75/jump-if-not-equal $run-main/disp8 - 56 # . run-tests() - 57 e8/call run-tests/disp32 - 58 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 59 eb/jump $main:end/disp8 - 60 $run-main: - 61 # - otherwise read a program from stdin and emit its translation to stdout - 62 # var ed/EAX : exit-descriptor - 63 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 64 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - 65 # configure ed to really exit() - 66 # . ed->target = 0 - 67 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 68 # return compile(Stdin, 1/stdout, 2/stderr, ed) - 69 # . . push args - 70 50/push-EAX/ed - 71 68/push 2/imm32/stderr - 72 68/push 1/imm32/stdout - 73 68/push Stdin/imm32 - 74 # . . call - 75 e8/call compile/disp32 - 76 # . . discard args - 77 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 78 # . syscall(exit, 0) - 79 bb/copy-to-EBX 0/imm32 - 80 $main:end: - 81 b8/copy-to-EAX 1/imm32/exit - 82 cd/syscall 0x80/imm8 - 83 - 84 # the main entry point - 85 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - 86 # . prolog - 87 55/push-EBP - 88 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 89 # . save registers - 90 50/push-EAX - 91 51/push-ECX - 92 # prime the pump - 93 # . Look = get-char(in) - 94 # . . push args - 95 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 96 # . . call - 97 e8/call get-char/disp32 - 98 # . . discard args - 99 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -100 # var num/ECX : (address stream) on the stack -101 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. -102 # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. -103 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. -104 # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. -105 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP -106 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -107 # initialize the stream -108 # . num->length = 7 -109 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) -110 # . clear-stream(num) -111 # . . push args -112 51/push-ECX -113 # . . call -114 e8/call clear-stream/disp32 -115 # . . discard args -116 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -117 # read a digit from 'in' into 'num' -118 # . get-num(in, num, err, ed) -119 # . . push args -120 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -121 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -122 51/push-ECX/num -123 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -124 # . . call -125 e8/call get-num/disp32 -126 # . . discard args -127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -128 # render 'num' into the following template on 'out': -129 # bb/copy-to-EBX _num_ -130 # b8/copy-to-EAX 1/imm32/exit -131 # cd/syscall 0x80/imm8 -132 # -133 # . write(out, "bb/copy-to-EBX ") -134 # . . push args -135 68/push "bb/copy-to-EBX "/imm32 -136 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -137 # . . call -138 e8/call write/disp32 -139 # . . discard args -140 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -141 # . write-stream(out, num) -142 # . . push args -143 51/push-ECX/num -144 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -145 # . . call -146 e8/call write-stream/disp32 -147 # . . discard args -148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -149 # . write(out, Newline) -150 # . . push args -151 68/push Newline/imm32 -152 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -153 # . . call -154 e8/call write/disp32 -155 # . . discard args -156 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -157 # . write(out, "b8/copy-to-EAX 1/imm32/exit\n") -158 # . . push args -159 68/push "b8/copy-to-EAX 1/imm32/exit\n"/imm32 -160 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -161 # . . call -162 e8/call write/disp32 -163 # . . discard args -164 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -165 # . write(out, "cd/syscall 0x80/imm8\n") -166 # . . push args -167 68/push "cd/syscall 0x80/imm8\n"/imm32 -168 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -169 # . . call -170 e8/call write/disp32 -171 # . . discard args -172 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -173 $compile:end: -174 # . restore registers -175 59/pop-to-ECX -176 58/pop-to-EAX -177 # . epilog -178 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -179 5d/pop-to-EBP -180 c3/return -181 -182 # Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. -183 # Input comes from the global variable 'Look', and we leave the next byte from -184 # 'in' into it on exit. -185 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> -186 # pseudocode: -187 # if (!is-digit?(Look)) expected(ed, err, "integer") -188 # if out->write >= out->length -189 # write(err, "Error: too many digits in number\n") -190 # stop(ed, 1) -191 # out->data[out->write] = LSB(Look) -192 # ++out->write -193 # Look = get-char(in) -194 # -195 # registers: -196 # in: ESI -197 # out: EDI -198 # out->write: ECX (cached copy; need to keep in sync) -199 # out->length: EDX -200 # temporaries: EAX, EBX -201 # We can't allocate Look to a register because it gets written implicitly in -202 # get-char in each iteration of the loop. (Thereby demonstrating that it's -203 # not the right interface for us. But we'll keep it just to follow Crenshaw.) -204 # -205 # . prolog -206 55/push-EBP -207 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -208 # - if (is-digit?(Look)) expected(ed, err, "integer") -209 # . EAX = is-digit?(Look) -210 # . . push args -211 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look -212 # . . call -213 e8/call is-digit?/disp32 -214 # . . discard args -215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -216 # . if (EAX == 0) -217 3d/compare-EAX-and 0/imm32 -218 75/jump-if-not-equal $get-num:main/disp8 -219 # . expected(ed, err, "integer") -220 # . . push args -221 68/push "integer"/imm32 -222 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -223 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -224 # . . call -225 e8/call expected/disp32 # never returns -226 # . . discard args -227 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -228 $get-num:main: -229 # - otherwise read a digit -230 # . save registers -231 50/push-EAX -232 51/push-ECX -233 52/push-EDX -234 53/push-EBX -235 56/push-ESI -236 57/push-EDI -237 # read necessary variables to registers -238 # ESI = in -239 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -240 # EDI = out -241 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -242 # ECX = out->write -243 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX -244 # EDX = out->length -245 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX -246 # if (out->write >= out->length) error -247 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX -248 7d/jump-if-lesser $get-num:stage2/disp8 -249 # . error(ed, err, msg) # TODO: show full number -250 # . . push args -251 68/push "get-num: too many digits in number"/imm32 -252 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -253 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -254 # . . call -255 e8/call error/disp32 # never returns -256 # . . discard args -257 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -258 $get-num:stage2: -259 # out->data[out->write] = LSB(Look) -260 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX -261 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX -262 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX -263 # ++out->write -264 41/increment-ECX -265 # Look = get-char(in) -266 # . . push args -267 56/push-ESI -268 # . . call -269 e8/call get-char/disp32 -270 # . . discard args -271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -272 $get-num:loop-end: -273 # persist necessary variables from registers -274 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI -275 $get-num:end: -276 # . restore registers -277 5f/pop-to-EDI -278 5e/pop-to-ESI -279 5b/pop-to-EBX -280 5a/pop-to-EDX -281 59/pop-to-ECX -282 58/pop-to-EAX -283 # . epilog -284 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -285 5d/pop-to-EBP -286 c3/return -287 -288 test-get-num-reads-single-digit: -289 # - check that get-num returns first character if it's a digit -290 # This test uses exit-descriptors. Use EBP for setting up local variables. -291 55/push-EBP -292 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -293 # clear all streams -294 # . clear-stream(_test-stream) -295 # . . push args -296 68/push _test-stream/imm32 -297 # . . call -298 e8/call clear-stream/disp32 -299 # . . discard args -300 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -301 # . clear-stream(_test-buffered-file+4) -302 # . . push args -303 b8/copy-to-EAX _test-buffered-file/imm32 -304 05/add-to-EAX 4/imm32 -305 50/push-EAX -306 # . . call -307 e8/call clear-stream/disp32 -308 # . . discard args -309 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -310 # . clear-stream(_test-output-stream) -311 # . . push args -312 68/push _test-output-stream/imm32 -313 # . . call -314 e8/call clear-stream/disp32 -315 # . . discard args -316 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -317 # . clear-stream(_test-error-stream) -318 # . . push args -319 68/push _test-error-stream/imm32 -320 # . . call -321 e8/call clear-stream/disp32 -322 # . . discard args -323 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -324 # initialize 'in' -325 # . write(_test-stream, "3") -326 # . . push args -327 68/push "3"/imm32 -328 68/push _test-stream/imm32 -329 # . . call -330 e8/call write/disp32 -331 # . . discard args -332 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -333 # initialize exit-descriptor 'ed' for the call to 'get-num' below -334 # . var ed/EAX : exit-descriptor -335 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -336 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -337 # . tailor-exit-descriptor(ed, 16) -338 # . . push args -339 68/push 0x10/imm32/nbytes-of-args-for-get-num -340 50/push-EAX/ed -341 # . . call -342 e8/call tailor-exit-descriptor/disp32 -343 # . . discard args -344 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -345 # prime the pump -346 # . get-char(_test-buffered-file) -347 # . . push args -348 68/push _test-buffered-file/imm32 -349 # . . call -350 e8/call get-char/disp32 -351 # . . discard args -352 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -353 # get-num(in, out, err, ed) -354 # . . push args -355 50/push-EAX/ed -356 68/push _test-error-stream/imm32 -357 68/push _test-output-stream/imm32 -358 68/push _test-buffered-file/imm32 -359 # . . call -360 e8/call get-num/disp32 -361 # registers except ESP may be clobbered at this point -362 # . . discard args -363 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -364 # check-ints-equal(*_test-output-stream->data, '3', msg) -365 # . . push args -366 68/push "F - test-get-num-reads-single-digit"/imm32 -367 68/push 0x33/imm32 -368 b8/copy-to-EAX _test-output-stream/imm32 -369 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -370 # . . call -371 e8/call check-ints-equal/disp32 -372 # . . discard args -373 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -374 # . reclaim locals -375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -376 5d/pop-to-EBP -377 c3/return -378 -379 test-get-num-aborts-on-non-digit-in-Look: -380 # - check that get-num returns first character if it's a digit -381 # This test uses exit-descriptors. Use EBP for setting up local variables. -382 55/push-EBP -383 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -384 # clear all streams -385 # . clear-stream(_test-stream) -386 # . . push args -387 68/push _test-stream/imm32 -388 # . . call -389 e8/call clear-stream/disp32 -390 # . . discard args -391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -392 # . clear-stream(_test-buffered-file+4) -393 # . . push args -394 b8/copy-to-EAX _test-buffered-file/imm32 -395 05/add-to-EAX 4/imm32 -396 50/push-EAX -397 # . . call -398 e8/call clear-stream/disp32 -399 # . . discard args -400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -401 # . clear-stream(_test-output-stream) -402 # . . push args -403 68/push _test-output-stream/imm32 -404 # . . call -405 e8/call clear-stream/disp32 -406 # . . discard args -407 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -408 # . clear-stream(_test-error-stream) -409 # . . push args -410 68/push _test-error-stream/imm32 -411 # . . call -412 e8/call clear-stream/disp32 -413 # . . discard args -414 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -415 # initialize 'in' -416 # . write(_test-stream, "3") -417 # . . push args -418 68/push "3"/imm32 -419 68/push _test-stream/imm32 -420 # . . call -421 e8/call write/disp32 -422 # . . discard args -423 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -424 # initialize exit-descriptor 'ed' for the call to 'get-num' below -425 # . var ed/EAX : (address exit-descriptor) -426 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -427 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -428 # . tailor-exit-descriptor(ed, 16) -429 # . . push args -430 68/push 0x10/imm32/nbytes-of-args-for-get-num -431 50/push-EAX/ed -432 # . . call -433 e8/call tailor-exit-descriptor/disp32 -434 # . . discard args -435 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -436 # *don't* prime the pump -437 # get-num(in, out, err, ed) -438 # . . push args -439 50/push-EAX/ed -440 68/push _test-error-stream/imm32 -441 68/push _test-output-stream/imm32 -442 68/push _test-buffered-file/imm32 -443 # . . call -444 e8/call get-num/disp32 -445 # registers except ESP may be clobbered at this point -446 # . . discard args -447 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -448 # check that get-num tried to call exit(1) -449 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 -450 # . . push args -451 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 -452 68/push 2/imm32 -453 # . . push ed->value -454 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -455 # . . call -456 e8/call check-ints-equal/disp32 -457 # . . discard args -458 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -459 # . reclaim locals -460 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -461 5d/pop-to-EBP -462 c3/return -463 -464 ## helpers -465 -466 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) -467 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> -468 # . prolog -469 55/push-EBP -470 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -471 # write(f, "Error: ") -472 # . . push args -473 68/push "Error: "/imm32 -474 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -475 # . . call -476 e8/call write/disp32 -477 # . . discard args -478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -479 # write(f, s) -480 # . . push args -481 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -482 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -483 # . . call -484 e8/call write/disp32 -485 # . . discard args -486 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -487 # write(f, " expected") -488 # . . push args -489 68/push " expected\n"/imm32 -490 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -491 # . . call -492 e8/call write/disp32 -493 # . . discard args -494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -495 # stop(ed, 1) -496 # . . push args -497 68/push 1/imm32 -498 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -499 # . . call -500 e8/call stop/disp32 -501 # should never get past this point -502 $expected:dead-end: -503 # . epilog -504 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -505 5d/pop-to-EBP -506 c3/return -507 -508 # read a byte from 'f', and save it in 'Look' -509 get-char: # f : (address buffered-file) -> <void> -510 # . prolog -511 55/push-EBP -512 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -513 # . save registers -514 50/push-EAX -515 # EAX = read-byte-buffered(f) -516 # . . push args -517 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -518 # . . call -519 e8/call read-byte-buffered/disp32 -520 # . . discard args -521 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -522 # save EAX to Look -523 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -524 $get-char:end: -525 # . restore registers -526 58/pop-to-EAX -527 # . epilog -528 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -529 5d/pop-to-EBP -530 c3/return -531 -532 is-digit?: # c : int -> EAX : boolean -533 # . prolog -534 55/push-EBP -535 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -536 # EAX = false -537 b8/copy-to-EAX 0/imm32 -538 # if (c < '0') return false -539 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) -540 7c/jump-if-lesser $is-digit?:end/disp8 -541 # if (c > '9') return false -542 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) -543 7f/jump-if-greater $is-digit?:end/disp8 -544 # otherwise return true -545 b8/copy-to-EAX 1/imm32 -546 $is-digit?:end: -547 # . epilog -548 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -549 5d/pop-to-EBP -550 c3/return -551 -552 == data -553 -554 Look: # (char with some extra padding) -555 0/imm32 + 34 # initialize heap + 35 # . Heap = new-segment(64KB) + 36 # . . push args + 37 68/push Heap/imm32 + 38 68/push 0x10000/imm32/64KB + 39 # . . call + 40 e8/call new-segment/disp32 + 41 # . . discard args + 42 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 43 + 44 # . prolog + 45 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 46 # - if argc > 1 and argv[1] == "test", then return run_tests() + 47 # . argc > 1 + 48 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP + 49 7e/jump-if-lesser-or-equal $run-main/disp8 + 50 # . argv[1] == "test" + 51 # . . push args + 52 68/push "test"/imm32 + 53 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 54 # . . call + 55 e8/call kernel-string-equal?/disp32 + 56 # . . discard args + 57 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 58 # . check result + 59 3d/compare-EAX-and 1/imm32 + 60 75/jump-if-not-equal $run-main/disp8 + 61 # . run-tests() + 62 e8/call run-tests/disp32 + 63 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 64 eb/jump $main:end/disp8 + 65 $run-main: + 66 # - otherwise read a program from stdin and emit its translation to stdout + 67 # var ed/EAX : exit-descriptor + 68 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 69 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX + 70 # configure ed to really exit() + 71 # . ed->target = 0 + 72 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 73 # return compile(Stdin, 1/stdout, 2/stderr, ed) + 74 # . . push args + 75 50/push-EAX/ed + 76 68/push 2/imm32/stderr + 77 68/push 1/imm32/stdout + 78 68/push Stdin/imm32 + 79 # . . call + 80 e8/call compile/disp32 + 81 # . . discard args + 82 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 83 # . syscall(exit, 0) + 84 bb/copy-to-EBX 0/imm32 + 85 $main:end: + 86 b8/copy-to-EAX 1/imm32/exit + 87 cd/syscall 0x80/imm8 + 88 + 89 # the main entry point + 90 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> + 91 # . prolog + 92 55/push-EBP + 93 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 94 # . save registers + 95 50/push-EAX + 96 51/push-ECX + 97 # prime the pump + 98 # . Look = get-char(in) + 99 # . . push args +100 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +101 # . . call +102 e8/call get-char/disp32 +103 # . . discard args +104 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +105 # var num/ECX : (address stream) on the stack +106 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. +107 # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. +108 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. +109 # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. +110 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP +111 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +112 # initialize the stream +113 # . num->length = 7 +114 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) +115 # . clear-stream(num) +116 # . . push args +117 51/push-ECX +118 # . . call +119 e8/call clear-stream/disp32 +120 # . . discard args +121 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +122 # read a digit from 'in' into 'num' +123 # . get-num(in, num, err, ed) +124 # . . push args +125 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +126 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +127 51/push-ECX/num +128 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +129 # . . call +130 e8/call get-num/disp32 +131 # . . discard args +132 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +133 # render 'num' into the following template on 'out': +134 # bb/copy-to-EBX _num_ +135 # b8/copy-to-EAX 1/imm32/exit +136 # cd/syscall 0x80/imm8 +137 # +138 # . write(out, "bb/copy-to-EBX ") +139 # . . push args +140 68/push "bb/copy-to-EBX "/imm32 +141 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +142 # . . call +143 e8/call write/disp32 +144 # . . discard args +145 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +146 # . write-stream(out, num) +147 # . . push args +148 51/push-ECX/num +149 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +150 # . . call +151 e8/call write-stream/disp32 +152 # . . discard args +153 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +154 # . write(out, Newline) +155 # . . push args +156 68/push Newline/imm32 +157 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +158 # . . call +159 e8/call write/disp32 +160 # . . discard args +161 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +162 # . write(out, "b8/copy-to-EAX 1/imm32/exit\n") +163 # . . push args +164 68/push "b8/copy-to-EAX 1/imm32/exit\n"/imm32 +165 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +166 # . . call +167 e8/call write/disp32 +168 # . . discard args +169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +170 # . write(out, "cd/syscall 0x80/imm8\n") +171 # . . push args +172 68/push "cd/syscall 0x80/imm8\n"/imm32 +173 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +174 # . . call +175 e8/call write/disp32 +176 # . . discard args +177 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +178 $compile:end: +179 # . restore registers +180 59/pop-to-ECX +181 58/pop-to-EAX +182 # . epilog +183 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +184 5d/pop-to-EBP +185 c3/return +186 +187 # Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. +188 # Input comes from the global variable 'Look', and we leave the next byte from +189 # 'in' into it on exit. +190 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> +191 # pseudocode: +192 # if (!is-digit?(Look)) expected(ed, err, "integer") +193 # if out->write >= out->length +194 # write(err, "Error: too many digits in number\n") +195 # stop(ed, 1) +196 # out->data[out->write] = LSB(Look) +197 # ++out->write +198 # Look = get-char(in) +199 # +200 # registers: +201 # in: ESI +202 # out: EDI +203 # out->write: ECX (cached copy; need to keep in sync) +204 # out->length: EDX +205 # temporaries: EAX, EBX +206 # We can't allocate Look to a register because it gets written implicitly in +207 # get-char in each iteration of the loop. (Thereby demonstrating that it's +208 # not the right interface for us. But we'll keep it just to follow Crenshaw.) +209 # +210 # . prolog +211 55/push-EBP +212 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +213 # - if (is-digit?(Look)) expected(ed, err, "integer") +214 # . EAX = is-digit?(Look) +215 # . . push args +216 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +217 # . . call +218 e8/call is-digit?/disp32 +219 # . . discard args +220 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +221 # . if (EAX == 0) +222 3d/compare-EAX-and 0/imm32 +223 75/jump-if-not-equal $get-num:main/disp8 +224 # . expected(ed, err, "integer") +225 # . . push args +226 68/push "integer"/imm32 +227 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +228 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +229 # . . call +230 e8/call expected/disp32 # never returns +231 # . . discard args +232 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +233 $get-num:main: +234 # - otherwise read a digit +235 # . save registers +236 50/push-EAX +237 51/push-ECX +238 52/push-EDX +239 53/push-EBX +240 56/push-ESI +241 57/push-EDI +242 # read necessary variables to registers +243 # ESI = in +244 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +245 # EDI = out +246 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI +247 # ECX = out->write +248 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX +249 # EDX = out->length +250 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX +251 # if (out->write >= out->length) error +252 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX +253 7d/jump-if-lesser $get-num:stage2/disp8 +254 # . error(ed, err, msg) # TODO: show full number +255 # . . push args +256 68/push "get-num: too many digits in number"/imm32 +257 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +258 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +259 # . . call +260 e8/call error/disp32 # never returns +261 # . . discard args +262 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +263 $get-num:stage2: +264 # out->data[out->write] = LSB(Look) +265 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX +266 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX +267 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX +268 # ++out->write +269 41/increment-ECX +270 # Look = get-char(in) +271 # . . push args +272 56/push-ESI +273 # . . call +274 e8/call get-char/disp32 +275 # . . discard args +276 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +277 $get-num:loop-end: +278 # persist necessary variables from registers +279 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI +280 $get-num:end: +281 # . restore registers +282 5f/pop-to-EDI +283 5e/pop-to-ESI +284 5b/pop-to-EBX +285 5a/pop-to-EDX +286 59/pop-to-ECX +287 58/pop-to-EAX +288 # . epilog +289 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +290 5d/pop-to-EBP +291 c3/return +292 +293 test-get-num-reads-single-digit: +294 # - check that get-num returns first character if it's a digit +295 # This test uses exit-descriptors. Use EBP for setting up local variables. +296 55/push-EBP +297 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +298 # clear all streams +299 # . clear-stream(_test-stream) +300 # . . push args +301 68/push _test-stream/imm32 +302 # . . call +303 e8/call clear-stream/disp32 +304 # . . discard args +305 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +306 # . clear-stream(_test-buffered-file+4) +307 # . . push args +308 b8/copy-to-EAX _test-buffered-file/imm32 +309 05/add-to-EAX 4/imm32 +310 50/push-EAX +311 # . . call +312 e8/call clear-stream/disp32 +313 # . . discard args +314 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +315 # . clear-stream(_test-output-stream) +316 # . . push args +317 68/push _test-output-stream/imm32 +318 # . . call +319 e8/call clear-stream/disp32 +320 # . . discard args +321 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +322 # . clear-stream(_test-error-stream) +323 # . . push args +324 68/push _test-error-stream/imm32 +325 # . . call +326 e8/call clear-stream/disp32 +327 # . . discard args +328 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +329 # initialize 'in' +330 # . write(_test-stream, "3") +331 # . . push args +332 68/push "3"/imm32 +333 68/push _test-stream/imm32 +334 # . . call +335 e8/call write/disp32 +336 # . . discard args +337 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +338 # initialize exit-descriptor 'ed' for the call to 'get-num' below +339 # . var ed/EAX : exit-descriptor +340 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +341 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +342 # . tailor-exit-descriptor(ed, 16) +343 # . . push args +344 68/push 0x10/imm32/nbytes-of-args-for-get-num +345 50/push-EAX/ed +346 # . . call +347 e8/call tailor-exit-descriptor/disp32 +348 # . . discard args +349 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +350 # prime the pump +351 # . get-char(_test-buffered-file) +352 # . . push args +353 68/push _test-buffered-file/imm32 +354 # . . call +355 e8/call get-char/disp32 +356 # . . discard args +357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +358 # get-num(in, out, err, ed) +359 # . . push args +360 50/push-EAX/ed +361 68/push _test-error-stream/imm32 +362 68/push _test-output-stream/imm32 +363 68/push _test-buffered-file/imm32 +364 # . . call +365 e8/call get-num/disp32 +366 # registers except ESP may be clobbered at this point +367 # . . discard args +368 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +369 # check-ints-equal(*_test-output-stream->data, '3', msg) +370 # . . push args +371 68/push "F - test-get-num-reads-single-digit"/imm32 +372 68/push 0x33/imm32 +373 b8/copy-to-EAX _test-output-stream/imm32 +374 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +375 # . . call +376 e8/call check-ints-equal/disp32 +377 # . . discard args +378 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +379 # . reclaim locals +380 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +381 5d/pop-to-EBP +382 c3/return +383 +384 test-get-num-aborts-on-non-digit-in-Look: +385 # - check that get-num returns first character if it's a digit +386 # This test uses exit-descriptors. Use EBP for setting up local variables. +387 55/push-EBP +388 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +389 # clear all streams +390 # . clear-stream(_test-stream) +391 # . . push args +392 68/push _test-stream/imm32 +393 # . . call +394 e8/call clear-stream/disp32 +395 # . . discard args +396 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +397 # . clear-stream(_test-buffered-file+4) +398 # . . push args +399 b8/copy-to-EAX _test-buffered-file/imm32 +400 05/add-to-EAX 4/imm32 +401 50/push-EAX +402 # . . call +403 e8/call clear-stream/disp32 +404 # . . discard args +405 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +406 # . clear-stream(_test-output-stream) +407 # . . push args +408 68/push _test-output-stream/imm32 +409 # . . call +410 e8/call clear-stream/disp32 +411 # . . discard args +412 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +413 # . clear-stream(_test-error-stream) +414 # . . push args +415 68/push _test-error-stream/imm32 +416 # . . call +417 e8/call clear-stream/disp32 +418 # . . discard args +419 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +420 # initialize 'in' +421 # . write(_test-stream, "3") +422 # . . push args +423 68/push "3"/imm32 +424 68/push _test-stream/imm32 +425 # . . call +426 e8/call write/disp32 +427 # . . discard args +428 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +429 # initialize exit-descriptor 'ed' for the call to 'get-num' below +430 # . var ed/EAX : (address exit-descriptor) +431 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +432 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +433 # . tailor-exit-descriptor(ed, 16) +434 # . . push args +435 68/push 0x10/imm32/nbytes-of-args-for-get-num +436 50/push-EAX/ed +437 # . . call +438 e8/call tailor-exit-descriptor/disp32 +439 # . . discard args +440 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +441 # *don't* prime the pump +442 # get-num(in, out, err, ed) +443 # . . push args +444 50/push-EAX/ed +445 68/push _test-error-stream/imm32 +446 68/push _test-output-stream/imm32 +447 68/push _test-buffered-file/imm32 +448 # . . call +449 e8/call get-num/disp32 +450 # registers except ESP may be clobbered at this point +451 # . . discard args +452 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +453 # check that get-num tried to call exit(1) +454 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 +455 # . . push args +456 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 +457 68/push 2/imm32 +458 # . . push ed->value +459 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +460 # . . call +461 e8/call check-ints-equal/disp32 +462 # . . discard args +463 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +464 # . reclaim locals +465 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +466 5d/pop-to-EBP +467 c3/return +468 +469 ## helpers +470 +471 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) +472 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> +473 # . prolog +474 55/push-EBP +475 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +476 # write(f, "Error: ") +477 # . . push args +478 68/push "Error: "/imm32 +479 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +480 # . . call +481 e8/call write/disp32 +482 # . . discard args +483 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +484 # write(f, s) +485 # . . push args +486 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +487 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +488 # . . call +489 e8/call write/disp32 +490 # . . discard args +491 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +492 # write(f, " expected") +493 # . . push args +494 68/push " expected\n"/imm32 +495 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +496 # . . call +497 e8/call write/disp32 +498 # . . discard args +499 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +500 # stop(ed, 1) +501 # . . push args +502 68/push 1/imm32 +503 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +504 # . . call +505 e8/call stop/disp32 +506 # should never get past this point +507 $expected:dead-end: +508 # . epilog +509 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +510 5d/pop-to-EBP +511 c3/return +512 +513 # read a byte from 'f', and save it in 'Look' +514 get-char: # f : (address buffered-file) -> <void> +515 # . prolog +516 55/push-EBP +517 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +518 # . save registers +519 50/push-EAX +520 # EAX = read-byte-buffered(f) +521 # . . push args +522 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +523 # . . call +524 e8/call read-byte-buffered/disp32 +525 # . . discard args +526 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +527 # save EAX to Look +528 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look +529 $get-char:end: +530 # . restore registers +531 58/pop-to-EAX +532 # . epilog +533 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +534 5d/pop-to-EBP +535 c3/return +536 +537 is-digit?: # c : int -> EAX : boolean +538 # . prolog +539 55/push-EBP +540 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +541 # EAX = false +542 b8/copy-to-EAX 0/imm32 +543 # if (c < '0') return false +544 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) +545 7c/jump-if-lesser $is-digit?:end/disp8 +546 # if (c > '9') return false +547 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) +548 7f/jump-if-greater $is-digit?:end/disp8 +549 # otherwise return true +550 b8/copy-to-EAX 1/imm32 +551 $is-digit?:end: +552 # . epilog +553 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +554 5d/pop-to-EBP +555 c3/return 556 -557 _test-output-stream: -558 # current write index -559 0/imm32 -560 # current read index -561 0/imm32 -562 # length -563 8/imm32 -564 # data -565 00 00 00 00 00 00 00 00 # 8 bytes -566 -567 _test-error-stream: -568 # current write index -569 0/imm32 -570 # current read index -571 0/imm32 -572 # length -573 0x40/imm32 -574 # data (4 lines x 16 bytes/line) -575 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -576 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -577 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -578 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -579 -580 # . . vim:nowrap:textwidth=0 +557 == data +558 +559 Look: # (char with some extra padding) +560 0/imm32 +561 +562 _test-output-stream: +563 # current write index +564 0/imm32 +565 # current read index +566 0/imm32 +567 # length +568 8/imm32 +569 # data +570 00 00 00 00 00 00 00 00 # 8 bytes +571 +572 _test-error-stream: +573 # current write index +574 0/imm32 +575 # current read index +576 0/imm32 +577 # length +578 0x40/imm32 +579 # data (4 lines x 16 bytes/line) +580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +581 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +582 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +583 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +584 +585 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/crenshaw2-1b.subx.html b/html/subx/apps/crenshaw2-1b.subx.html index 92ae9cd8..e4e147a8 100644 --- a/html/subx/apps/crenshaw2-1b.subx.html +++ b/html/subx/apps/crenshaw2-1b.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/crenshaw2-1b.subx - - + + @@ -14,18 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxMinorFunction { color: #875f5f; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -42,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -94,753 +93,758 @@ if ('onhashchange' in window) { 31 # . 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 32 33 Entry: # run tests if necessary, call 'compile' if not - 34 - 35 #? # for debugging: run a single test; don't bother setting status code - 36 #? e8/call test-get-num-reads-single-digit/disp32 - 37 #? eb/jump $main:end/disp8 - 38 - 39 # . prolog - 40 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 41 # - if argc > 1 and argv[1] == "test", then return run_tests() - 42 # . argc > 1 - 43 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 44 7e/jump-if-lesser-or-equal $run-main/disp8 - 45 # . argv[1] == "test" - 46 # . . push args - 47 68/push "test"/imm32 - 48 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 49 # . . call - 50 e8/call kernel-string-equal?/disp32 - 51 # . . discard args - 52 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 53 # . check result - 54 3d/compare-EAX-and 1/imm32 - 55 75/jump-if-not-equal $run-main/disp8 - 56 # . run-tests() - 57 e8/call run-tests/disp32 - 58 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 59 eb/jump $main:end/disp8 - 60 $run-main: - 61 # - otherwise read a program from stdin and emit its translation to stdout - 62 # var ed/EAX : exit-descriptor - 63 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 64 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - 65 # configure ed to really exit() - 66 # . ed->target = 0 - 67 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 68 # return compile(Stdin, 1/stdout, 2/stderr, ed) - 69 # . . push args - 70 50/push-EAX/ed - 71 68/push 2/imm32/stderr - 72 68/push 1/imm32/stdout - 73 68/push Stdin/imm32 - 74 # . . call - 75 e8/call compile/disp32 - 76 # . . discard args - 77 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 78 # . syscall(exit, 0) - 79 bb/copy-to-EBX 0/imm32 - 80 $main:end: - 81 b8/copy-to-EAX 1/imm32/exit - 82 cd/syscall 0x80/imm8 - 83 - 84 # the main entry point - 85 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - 86 # . prolog - 87 55/push-EBP - 88 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 89 # . save registers - 90 50/push-EAX - 91 51/push-ECX - 92 # prime the pump - 93 # . Look = get-char(in) - 94 # . . push args - 95 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 96 # . . call - 97 e8/call get-char/disp32 - 98 # . . discard args - 99 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -100 # var num/ECX : (address stream) on the stack -101 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. -102 # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. -103 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. -104 # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. -105 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP -106 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -107 # initialize the stream -108 # . num->length = 7 -109 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) -110 # . clear-stream(num) -111 # . . push args -112 51/push-ECX -113 # . . call -114 e8/call clear-stream/disp32 -115 # . . discard args -116 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -117 # read a digit from 'in' into 'num' -118 # . get-num(in, num, err, ed) -119 # . . push args -120 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -121 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -122 51/push-ECX/num -123 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -124 # . . call -125 e8/call get-num/disp32 -126 # . . discard args -127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -128 # render 'num' into the following template on 'out': -129 # bb/copy-to-EBX _num_ -130 # b8/copy-to-EAX 1/imm32/exit -131 # cd/syscall 0x80/imm8 -132 # -133 # . write(out, "bb/copy-to-EBX ") -134 # . . push args -135 68/push "bb/copy-to-EBX "/imm32 -136 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -137 # . . call -138 e8/call write/disp32 -139 # . . discard args -140 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -141 # . write-stream(out, num) -142 # . . push args -143 51/push-ECX/num -144 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -145 # . . call -146 e8/call write-stream/disp32 -147 # . . discard args -148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -149 # . write(out, Newline) -150 # . . push args -151 68/push Newline/imm32 -152 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -153 # . . call -154 e8/call write/disp32 -155 # . . discard args -156 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -157 # . write(out, "b8/copy-to-EAX 1/imm32/exit\n") -158 # . . push args -159 68/push "b8/copy-to-EAX 1/imm32/exit\n"/imm32 -160 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -161 # . . call -162 e8/call write/disp32 -163 # . . discard args -164 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -165 # . write(out, "cd/syscall 0x80/imm8\n") -166 # . . push args -167 68/push "cd/syscall 0x80/imm8\n"/imm32 -168 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -169 # . . call -170 e8/call write/disp32 -171 # . . discard args -172 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -173 $compile:end: -174 # . restore registers -175 59/pop-to-ECX -176 58/pop-to-EAX -177 # . epilog -178 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -179 5d/pop-to-EBP -180 c3/return -181 -182 # Read a sequence of digits into 'out'. Abort if there are none, or if there is -183 # no space in 'out'. -184 # Input comes from the global variable 'Look' (first byte) and the argument -185 # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. -186 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> -187 # pseudocode: -188 # if (!is-digit?(Look)) expected(ed, err, "integer") -189 # do -190 # if out->write >= out->length -191 # write(err, "Error: too many digits in number\n") -192 # stop(ed, 1) -193 # out->data[out->write] = LSB(Look) -194 # ++out->write -195 # Look = get-char(in) -196 # while is-digit?(Look) -197 # This is complicated because I don't want to hard-code the error strategy in -198 # a general helper like write-byte-buffered. Maybe I should just create a -199 # local helper. -200 # -201 # within the loop we'll try to keep things in registers: -202 # in: ESI -203 # out: EDI -204 # out->write: ECX (cached copy; need to keep in sync) -205 # out->length: EDX -206 # temporaries: EAX, EBX -207 # We can't allocate Look to a register because it gets written implicitly in -208 # get-char in each iteration of the loop. (Thereby demonstrating that it's -209 # not the right interface for us. But we'll keep it just to follow Crenshaw.) -210 # -211 # . prolog -212 55/push-EBP -213 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -214 # - if (is-digit?(Look)) expected(ed, err, "integer") -215 # . EAX = is-digit?(Look) -216 # . . push args -217 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look -218 # . . call -219 e8/call is-digit?/disp32 -220 # . . discard args -221 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -222 # . if (EAX == 0) -223 3d/compare-EAX-and 0/imm32 -224 75/jump-if-not-equal $get-num:main/disp8 -225 # . expected(ed, err, "integer") -226 # . . push args -227 68/push "integer"/imm32 -228 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -229 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -230 # . . call -231 e8/call expected/disp32 # never returns -232 # . . discard args -233 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -234 $get-num:main: -235 # - otherwise read a digit -236 # . save registers -237 50/push-EAX -238 51/push-ECX -239 52/push-EDX -240 53/push-EBX -241 56/push-ESI -242 57/push-EDI -243 # read necessary variables to registers -244 # ESI = in -245 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -246 # EDI = out -247 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -248 # ECX = out->write -249 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX -250 # EDX = out->length -251 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX -252 $get-num:loop: -253 # if (out->write >= out->length) error -254 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX -255 7d/jump-if-lesser $get-num:loop-stage2/disp8 -256 # . error(ed, err, msg) # TODO: show full number -257 # . . push args -258 68/push "get-num: too many digits in number"/imm32 -259 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -260 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -261 # . . call -262 e8/call error/disp32 # never returns -263 # . . discard args -264 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -265 $get-num:loop-stage2: -266 # out->data[out->write] = LSB(Look) -267 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX -268 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX -269 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX -270 # ++out->write -271 41/increment-ECX -272 # Look = get-char(in) -273 # . . push args -274 56/push-ESI -275 # . . call -276 e8/call get-char/disp32 -277 # . . discard args -278 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -279 # if (is-digit?(Look)) loop -280 # . EAX = is-digit?(Look) -281 # . . push args -282 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look -283 # . . call -284 e8/call is-digit?/disp32 -285 # . . discard args -286 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -287 # . if (EAX != 0) loop -288 3d/compare-EAX-and 0/imm32 -289 0f 85/jump-if-not-equal $get-num:loop/disp32 -290 $get-num:loop-end: -291 # persist necessary variables from registers -292 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI -293 $get-num:end: -294 # . restore registers -295 5f/pop-to-EDI -296 5e/pop-to-ESI -297 5b/pop-to-EBX -298 5a/pop-to-EDX -299 59/pop-to-ECX -300 58/pop-to-EAX -301 # . epilog -302 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -303 5d/pop-to-EBP -304 c3/return -305 -306 test-get-num-reads-single-digit: -307 # - check that get-num returns first character if it's a digit -308 # This test uses exit-descriptors. Use EBP for setting up local variables. -309 55/push-EBP -310 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -311 # clear all streams -312 # . clear-stream(_test-stream) -313 # . . push args -314 68/push _test-stream/imm32 -315 # . . call -316 e8/call clear-stream/disp32 -317 # . . discard args -318 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -319 # . clear-stream(_test-buffered-file+4) -320 # . . push args -321 b8/copy-to-EAX _test-buffered-file/imm32 -322 05/add-to-EAX 4/imm32 -323 50/push-EAX -324 # . . call -325 e8/call clear-stream/disp32 -326 # . . discard args -327 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -328 # . clear-stream(_test-output-stream) -329 # . . push args -330 68/push _test-output-stream/imm32 -331 # . . call -332 e8/call clear-stream/disp32 -333 # . . discard args -334 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -335 # . clear-stream(_test-error-stream) -336 # . . push args -337 68/push _test-error-stream/imm32 -338 # . . call -339 e8/call clear-stream/disp32 -340 # . . discard args -341 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -342 # initialize 'in' -343 # . write(_test-stream, "3") -344 # . . push args -345 68/push "3"/imm32 -346 68/push _test-stream/imm32 -347 # . . call -348 e8/call write/disp32 -349 # . . discard args -350 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -351 # initialize exit-descriptor 'ed' for the call to 'get-num' below -352 # . var ed/EAX : exit-descriptor -353 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -354 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -355 # . tailor-exit-descriptor(ed, 16) -356 # . . push args -357 68/push 0x10/imm32/nbytes-of-args-for-get-num -358 50/push-EAX/ed -359 # . . call -360 e8/call tailor-exit-descriptor/disp32 -361 # . . discard args -362 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -363 # prime the pump -364 # . get-char(_test-buffered-file) -365 # . . push args -366 68/push _test-buffered-file/imm32 -367 # . . call -368 e8/call get-char/disp32 -369 # . . discard args -370 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -371 # get-num(in, out, err, ed) -372 # . . push args -373 50/push-EAX/ed -374 68/push _test-error-stream/imm32 -375 68/push _test-output-stream/imm32 -376 68/push _test-buffered-file/imm32 -377 # . . call -378 e8/call get-num/disp32 -379 # registers except ESP may be clobbered at this point -380 # . . discard args -381 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -382 # check-ints-equal(*_test-output-stream->data, '3', msg) -383 # . . push args -384 68/push "F - test-get-num-reads-single-digit"/imm32 -385 68/push 0x33/imm32 -386 b8/copy-to-EAX _test-output-stream/imm32 -387 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -388 # . . call -389 e8/call check-ints-equal/disp32 -390 # . . discard args -391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -392 # . reclaim locals -393 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -394 5d/pop-to-EBP -395 c3/return -396 -397 test-get-num-aborts-on-non-digit-in-Look: -398 # - check that get-num returns first character if it's a digit -399 # This test uses exit-descriptors. Use EBP for setting up local variables. -400 55/push-EBP -401 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -402 # clear all streams -403 # . clear-stream(_test-stream) -404 # . . push args -405 68/push _test-stream/imm32 -406 # . . call -407 e8/call clear-stream/disp32 -408 # . . discard args -409 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -410 # . clear-stream(_test-buffered-file+4) -411 # . . push args -412 b8/copy-to-EAX _test-buffered-file/imm32 -413 05/add-to-EAX 4/imm32 -414 50/push-EAX -415 # . . call -416 e8/call clear-stream/disp32 -417 # . . discard args -418 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -419 # . clear-stream(_test-output-stream) -420 # . . push args -421 68/push _test-output-stream/imm32 -422 # . . call -423 e8/call clear-stream/disp32 -424 # . . discard args -425 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -426 # . clear-stream(_test-error-stream) -427 # . . push args -428 68/push _test-error-stream/imm32 -429 # . . call -430 e8/call clear-stream/disp32 -431 # . . discard args -432 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -433 # initialize 'in' -434 # . write(_test-stream, "3") -435 # . . push args -436 68/push "3"/imm32 -437 68/push _test-stream/imm32 -438 # . . call -439 e8/call write/disp32 -440 # . . discard args -441 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -442 # initialize exit-descriptor 'ed' for the call to 'get-num' below -443 # . var ed/EAX : (address exit-descriptor) -444 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -445 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -446 # . tailor-exit-descriptor(ed, 16) -447 # . . push args -448 68/push 0x10/imm32/nbytes-of-args-for-get-num -449 50/push-EAX/ed -450 # . . call -451 e8/call tailor-exit-descriptor/disp32 -452 # . . discard args -453 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -454 # *don't* prime the pump -455 # get-num(in, out, err, ed) -456 # . . push args -457 50/push-EAX/ed -458 68/push _test-error-stream/imm32 -459 68/push _test-output-stream/imm32 -460 68/push _test-buffered-file/imm32 -461 # . . call -462 e8/call get-num/disp32 -463 # registers except ESP may be clobbered at this point -464 # . . discard args -465 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -466 # check that get-num tried to call exit(1) -467 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 -468 # . . push args -469 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 -470 68/push 2/imm32 -471 # . . push ed->value -472 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -473 # . . call -474 e8/call check-ints-equal/disp32 -475 # . . discard args -476 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -477 # . reclaim locals -478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -479 5d/pop-to-EBP -480 c3/return -481 -482 test-get-num-reads-multiple-digits: -483 # - check that get-num returns all initial digits until it encounters a non-digit -484 # This test uses exit-descriptors. Use EBP for setting up local variables. -485 55/push-EBP -486 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -487 # clear all streams -488 # . clear-stream(_test-stream) -489 # . . push args -490 68/push _test-stream/imm32 -491 # . . call -492 e8/call clear-stream/disp32 -493 # . . discard args -494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -495 # . clear-stream(_test-buffered-file+4) -496 # . . push args -497 b8/copy-to-EAX _test-buffered-file/imm32 -498 05/add-to-EAX 4/imm32 -499 50/push-EAX -500 # . . call -501 e8/call clear-stream/disp32 -502 # . . discard args -503 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -504 # . clear-stream(_test-output-stream) -505 # . . push args -506 68/push _test-output-stream/imm32 -507 # . . call -508 e8/call clear-stream/disp32 -509 # . . discard args -510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -511 # . clear-stream(_test-error-stream) -512 # . . push args -513 68/push _test-error-stream/imm32 -514 # . . call -515 e8/call clear-stream/disp32 -516 # . . discard args -517 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -518 # initialize 'in' -519 # . write(_test-stream, "3456 x") -520 # . . push args -521 68/push "3456"/imm32 -522 68/push _test-stream/imm32 -523 # . . call -524 e8/call write/disp32 -525 # . . discard args -526 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -527 # initialize exit-descriptor 'ed' for the call to 'get-num' below -528 # . var ed/EAX : (address exit-descriptor) -529 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -530 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -531 # . tailor-exit-descriptor(ed, 16) -532 # . . push args -533 68/push 0x10/imm32/nbytes-of-args-for-get-num -534 50/push-EAX/ed -535 # . . call -536 e8/call tailor-exit-descriptor/disp32 -537 # . . discard args -538 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -539 # prime the pump -540 # . get-char(_test-buffered-file) -541 # . . push args -542 68/push _test-buffered-file/imm32 -543 # . . call -544 e8/call get-char/disp32 -545 # . . discard args -546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -547 # get-num(in, out, err, ed) -548 # . . push args -549 50/push-EAX/ed -550 68/push _test-error-stream/imm32 -551 68/push _test-output-stream/imm32 -552 68/push _test-buffered-file/imm32 -553 # . . call -554 e8/call get-num/disp32 -555 # registers except ESP may be clobbered at this point -556 # . . discard args -557 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -558 # check-ints-equal(*_test-output-stream->data, '3456', msg) -559 # . . push args -560 68/push "F - test-get-num-reads-multiple-digits"/imm32 -561 68/push 0x36353433/imm32 -562 b8/copy-to-EAX _test-output-stream/imm32 -563 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -564 # . . call -565 e8/call check-ints-equal/disp32 -566 # . . discard args -567 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -568 # . reclaim locals -569 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -570 5d/pop-to-EBP -571 c3/return -572 -573 test-get-num-reads-multiple-digits-followed-by-nondigit: -574 # - check that get-num returns all initial digits until it encounters a non-digit -575 # This test uses exit-descriptors. Use EBP for setting up local variables. -576 55/push-EBP -577 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -578 # clear all streams -579 # . clear-stream(_test-stream) -580 # . . push args -581 68/push _test-stream/imm32 -582 # . . call -583 e8/call clear-stream/disp32 -584 # . . discard args -585 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -586 # . clear-stream(_test-buffered-file+4) -587 # . . push args -588 b8/copy-to-EAX _test-buffered-file/imm32 -589 05/add-to-EAX 4/imm32 -590 50/push-EAX -591 # . . call -592 e8/call clear-stream/disp32 -593 # . . discard args -594 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -595 # . clear-stream(_test-output-stream) -596 # . . push args -597 68/push _test-output-stream/imm32 -598 # . . call -599 e8/call clear-stream/disp32 -600 # . . discard args -601 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -602 # . clear-stream(_test-error-stream) -603 # . . push args -604 68/push _test-error-stream/imm32 -605 # . . call -606 e8/call clear-stream/disp32 -607 # . . discard args -608 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -609 # initialize 'in' -610 # . write(_test-stream, "3456 x") -611 # . . push args -612 68/push "3456 x"/imm32 -613 68/push _test-stream/imm32 -614 # . . call -615 e8/call write/disp32 -616 # . . discard args -617 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -618 # initialize exit-descriptor 'ed' for the call to 'get-num' below -619 # . var ed/EAX : (address exit-descriptor) -620 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -621 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -622 # . tailor-exit-descriptor(ed, 16) -623 # . . push args -624 68/push 0x10/imm32/nbytes-of-args-for-get-num -625 50/push-EAX/ed -626 # . . call -627 e8/call tailor-exit-descriptor/disp32 -628 # . . discard args -629 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -630 # prime the pump -631 # . get-char(_test-buffered-file) -632 # . . push args -633 68/push _test-buffered-file/imm32 -634 # . . call -635 e8/call get-char/disp32 -636 # . . discard args -637 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -638 # get-num(in, out, err, ed) -639 # . . push args -640 50/push-EAX/ed -641 68/push _test-error-stream/imm32 -642 68/push _test-output-stream/imm32 -643 68/push _test-buffered-file/imm32 -644 # . . call -645 e8/call get-num/disp32 -646 # registers except ESP may be clobbered at this point -647 # . . discard args -648 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -649 # check-ints-equal(*_test-output-stream->data, '3456', msg) -650 # . . push args -651 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 -652 68/push 0x36353433/imm32 -653 b8/copy-to-EAX _test-output-stream/imm32 -654 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -655 # . . call -656 e8/call check-ints-equal/disp32 -657 # . . discard args -658 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -659 # . reclaim locals -660 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -661 5d/pop-to-EBP -662 c3/return -663 -664 ## helpers -665 -666 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) -667 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> -668 # . prolog -669 55/push-EBP -670 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -671 # write(f, "Error: ") -672 # . . push args -673 68/push "Error: "/imm32 -674 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -675 # . . call -676 e8/call write/disp32 -677 # . . discard args -678 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -679 # write(f, s) -680 # . . push args -681 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -682 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -683 # . . call -684 e8/call write/disp32 -685 # . . discard args -686 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -687 # write(f, " expected\n") -688 # . . push args -689 68/push " expected\n"/imm32 -690 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -691 # . . call -692 e8/call write/disp32 -693 # . . discard args -694 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -695 # stop(ed, 1) -696 # . . push args -697 68/push 1/imm32 -698 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -699 # . . call -700 e8/call stop/disp32 -701 # should never get past this point -702 $expected:dead-end: -703 # . epilog -704 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -705 5d/pop-to-EBP -706 c3/return -707 -708 # read a byte from 'f', and save it in 'Look' -709 get-char: # f : (address buffered-file) -> <void> -710 # . prolog -711 55/push-EBP -712 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -713 # . save registers -714 50/push-EAX -715 # EAX = read-byte-buffered(f) -716 # . . push args -717 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -718 # . . call -719 e8/call read-byte-buffered/disp32 -720 # . . discard args -721 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -722 # save EAX to Look -723 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -724 $get-char:end: -725 # . restore registers -726 58/pop-to-EAX -727 # . epilog -728 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -729 5d/pop-to-EBP -730 c3/return -731 -732 is-digit?: # c : int -> EAX : boolean -733 # . prolog -734 55/push-EBP -735 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -736 # EAX = false -737 b8/copy-to-EAX 0/imm32 -738 # if (c < '0') return false -739 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) -740 7c/jump-if-lesser $is-digit?:end/disp8 -741 # if (c > '9') return false -742 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) -743 7f/jump-if-greater $is-digit?:end/disp8 -744 # otherwise return true -745 b8/copy-to-EAX 1/imm32 -746 $is-digit?:end: -747 # . epilog -748 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -749 5d/pop-to-EBP -750 c3/return -751 -752 == data -753 -754 Look: # (char with some extra padding) -755 0/imm32 + 34 # initialize heap + 35 # . Heap = new-segment(64KB) + 36 # . . push args + 37 68/push Heap/imm32 + 38 68/push 0x10000/imm32/64KB + 39 # . . call + 40 e8/call new-segment/disp32 + 41 # . . discard args + 42 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 43 + 44 # . prolog + 45 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 46 # - if argc > 1 and argv[1] == "test", then return run_tests() + 47 # . argc > 1 + 48 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP + 49 7e/jump-if-lesser-or-equal $run-main/disp8 + 50 # . argv[1] == "test" + 51 # . . push args + 52 68/push "test"/imm32 + 53 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 54 # . . call + 55 e8/call kernel-string-equal?/disp32 + 56 # . . discard args + 57 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 58 # . check result + 59 3d/compare-EAX-and 1/imm32 + 60 75/jump-if-not-equal $run-main/disp8 + 61 # . run-tests() + 62 e8/call run-tests/disp32 + 63 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 64 eb/jump $main:end/disp8 + 65 $run-main: + 66 # - otherwise read a program from stdin and emit its translation to stdout + 67 # var ed/EAX : exit-descriptor + 68 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 69 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX + 70 # configure ed to really exit() + 71 # . ed->target = 0 + 72 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 73 # return compile(Stdin, 1/stdout, 2/stderr, ed) + 74 # . . push args + 75 50/push-EAX/ed + 76 68/push 2/imm32/stderr + 77 68/push 1/imm32/stdout + 78 68/push Stdin/imm32 + 79 # . . call + 80 e8/call compile/disp32 + 81 # . . discard args + 82 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 83 # . syscall(exit, 0) + 84 bb/copy-to-EBX 0/imm32 + 85 $main:end: + 86 b8/copy-to-EAX 1/imm32/exit + 87 cd/syscall 0x80/imm8 + 88 + 89 # the main entry point + 90 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> + 91 # . prolog + 92 55/push-EBP + 93 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 94 # . save registers + 95 50/push-EAX + 96 51/push-ECX + 97 # prime the pump + 98 # . Look = get-char(in) + 99 # . . push args +100 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +101 # . . call +102 e8/call get-char/disp32 +103 # . . discard args +104 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +105 # var num/ECX : (address stream) on the stack +106 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. +107 # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. +108 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. +109 # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. +110 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP +111 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +112 # initialize the stream +113 # . num->length = 7 +114 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) +115 # . clear-stream(num) +116 # . . push args +117 51/push-ECX +118 # . . call +119 e8/call clear-stream/disp32 +120 # . . discard args +121 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +122 # read a digit from 'in' into 'num' +123 # . get-num(in, num, err, ed) +124 # . . push args +125 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +126 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +127 51/push-ECX/num +128 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +129 # . . call +130 e8/call get-num/disp32 +131 # . . discard args +132 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +133 # render 'num' into the following template on 'out': +134 # bb/copy-to-EBX _num_ +135 # b8/copy-to-EAX 1/imm32/exit +136 # cd/syscall 0x80/imm8 +137 # +138 # . write(out, "bb/copy-to-EBX ") +139 # . . push args +140 68/push "bb/copy-to-EBX "/imm32 +141 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +142 # . . call +143 e8/call write/disp32 +144 # . . discard args +145 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +146 # . write-stream(out, num) +147 # . . push args +148 51/push-ECX/num +149 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +150 # . . call +151 e8/call write-stream/disp32 +152 # . . discard args +153 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +154 # . write(out, Newline) +155 # . . push args +156 68/push Newline/imm32 +157 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +158 # . . call +159 e8/call write/disp32 +160 # . . discard args +161 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +162 # . write(out, "b8/copy-to-EAX 1/imm32/exit\n") +163 # . . push args +164 68/push "b8/copy-to-EAX 1/imm32/exit\n"/imm32 +165 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +166 # . . call +167 e8/call write/disp32 +168 # . . discard args +169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +170 # . write(out, "cd/syscall 0x80/imm8\n") +171 # . . push args +172 68/push "cd/syscall 0x80/imm8\n"/imm32 +173 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +174 # . . call +175 e8/call write/disp32 +176 # . . discard args +177 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +178 $compile:end: +179 # . restore registers +180 59/pop-to-ECX +181 58/pop-to-EAX +182 # . epilog +183 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +184 5d/pop-to-EBP +185 c3/return +186 +187 # Read a sequence of digits into 'out'. Abort if there are none, or if there is +188 # no space in 'out'. +189 # Input comes from the global variable 'Look' (first byte) and the argument +190 # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. +191 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> +192 # pseudocode: +193 # if (!is-digit?(Look)) expected(ed, err, "integer") +194 # do +195 # if out->write >= out->length +196 # write(err, "Error: too many digits in number\n") +197 # stop(ed, 1) +198 # out->data[out->write] = LSB(Look) +199 # ++out->write +200 # Look = get-char(in) +201 # while is-digit?(Look) +202 # This is complicated because I don't want to hard-code the error strategy in +203 # a general helper like write-byte-buffered. Maybe I should just create a +204 # local helper. +205 # +206 # within the loop we'll try to keep things in registers: +207 # in: ESI +208 # out: EDI +209 # out->write: ECX (cached copy; need to keep in sync) +210 # out->length: EDX +211 # temporaries: EAX, EBX +212 # We can't allocate Look to a register because it gets written implicitly in +213 # get-char in each iteration of the loop. (Thereby demonstrating that it's +214 # not the right interface for us. But we'll keep it just to follow Crenshaw.) +215 # +216 # . prolog +217 55/push-EBP +218 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +219 # - if (is-digit?(Look)) expected(ed, err, "integer") +220 # . EAX = is-digit?(Look) +221 # . . push args +222 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +223 # . . call +224 e8/call is-digit?/disp32 +225 # . . discard args +226 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +227 # . if (EAX == 0) +228 3d/compare-EAX-and 0/imm32 +229 75/jump-if-not-equal $get-num:main/disp8 +230 # . expected(ed, err, "integer") +231 # . . push args +232 68/push "integer"/imm32 +233 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +234 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +235 # . . call +236 e8/call expected/disp32 # never returns +237 # . . discard args +238 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +239 $get-num:main: +240 # - otherwise read a digit +241 # . save registers +242 50/push-EAX +243 51/push-ECX +244 52/push-EDX +245 53/push-EBX +246 56/push-ESI +247 57/push-EDI +248 # read necessary variables to registers +249 # ESI = in +250 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +251 # EDI = out +252 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI +253 # ECX = out->write +254 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX +255 # EDX = out->length +256 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX +257 $get-num:loop: +258 # if (out->write >= out->length) error +259 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX +260 7d/jump-if-lesser $get-num:loop-stage2/disp8 +261 # . error(ed, err, msg) # TODO: show full number +262 # . . push args +263 68/push "get-num: too many digits in number"/imm32 +264 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +265 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +266 # . . call +267 e8/call error/disp32 # never returns +268 # . . discard args +269 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +270 $get-num:loop-stage2: +271 # out->data[out->write] = LSB(Look) +272 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX +273 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX +274 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX +275 # ++out->write +276 41/increment-ECX +277 # Look = get-char(in) +278 # . . push args +279 56/push-ESI +280 # . . call +281 e8/call get-char/disp32 +282 # . . discard args +283 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +284 # if (is-digit?(Look)) loop +285 # . EAX = is-digit?(Look) +286 # . . push args +287 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +288 # . . call +289 e8/call is-digit?/disp32 +290 # . . discard args +291 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +292 # . if (EAX != 0) loop +293 3d/compare-EAX-and 0/imm32 +294 0f 85/jump-if-not-equal $get-num:loop/disp32 +295 $get-num:loop-end: +296 # persist necessary variables from registers +297 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI +298 $get-num:end: +299 # . restore registers +300 5f/pop-to-EDI +301 5e/pop-to-ESI +302 5b/pop-to-EBX +303 5a/pop-to-EDX +304 59/pop-to-ECX +305 58/pop-to-EAX +306 # . epilog +307 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +308 5d/pop-to-EBP +309 c3/return +310 +311 test-get-num-reads-single-digit: +312 # - check that get-num returns first character if it's a digit +313 # This test uses exit-descriptors. Use EBP for setting up local variables. +314 55/push-EBP +315 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +316 # clear all streams +317 # . clear-stream(_test-stream) +318 # . . push args +319 68/push _test-stream/imm32 +320 # . . call +321 e8/call clear-stream/disp32 +322 # . . discard args +323 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +324 # . clear-stream(_test-buffered-file+4) +325 # . . push args +326 b8/copy-to-EAX _test-buffered-file/imm32 +327 05/add-to-EAX 4/imm32 +328 50/push-EAX +329 # . . call +330 e8/call clear-stream/disp32 +331 # . . discard args +332 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +333 # . clear-stream(_test-output-stream) +334 # . . push args +335 68/push _test-output-stream/imm32 +336 # . . call +337 e8/call clear-stream/disp32 +338 # . . discard args +339 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +340 # . clear-stream(_test-error-stream) +341 # . . push args +342 68/push _test-error-stream/imm32 +343 # . . call +344 e8/call clear-stream/disp32 +345 # . . discard args +346 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +347 # initialize 'in' +348 # . write(_test-stream, "3") +349 # . . push args +350 68/push "3"/imm32 +351 68/push _test-stream/imm32 +352 # . . call +353 e8/call write/disp32 +354 # . . discard args +355 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +356 # initialize exit-descriptor 'ed' for the call to 'get-num' below +357 # . var ed/EAX : exit-descriptor +358 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +359 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +360 # . tailor-exit-descriptor(ed, 16) +361 # . . push args +362 68/push 0x10/imm32/nbytes-of-args-for-get-num +363 50/push-EAX/ed +364 # . . call +365 e8/call tailor-exit-descriptor/disp32 +366 # . . discard args +367 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +368 # prime the pump +369 # . get-char(_test-buffered-file) +370 # . . push args +371 68/push _test-buffered-file/imm32 +372 # . . call +373 e8/call get-char/disp32 +374 # . . discard args +375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +376 # get-num(in, out, err, ed) +377 # . . push args +378 50/push-EAX/ed +379 68/push _test-error-stream/imm32 +380 68/push _test-output-stream/imm32 +381 68/push _test-buffered-file/imm32 +382 # . . call +383 e8/call get-num/disp32 +384 # registers except ESP may be clobbered at this point +385 # . . discard args +386 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +387 # check-ints-equal(*_test-output-stream->data, '3', msg) +388 # . . push args +389 68/push "F - test-get-num-reads-single-digit"/imm32 +390 68/push 0x33/imm32 +391 b8/copy-to-EAX _test-output-stream/imm32 +392 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +393 # . . call +394 e8/call check-ints-equal/disp32 +395 # . . discard args +396 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +397 # . reclaim locals +398 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +399 5d/pop-to-EBP +400 c3/return +401 +402 test-get-num-aborts-on-non-digit-in-Look: +403 # - check that get-num returns first character if it's a digit +404 # This test uses exit-descriptors. Use EBP for setting up local variables. +405 55/push-EBP +406 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +407 # clear all streams +408 # . clear-stream(_test-stream) +409 # . . push args +410 68/push _test-stream/imm32 +411 # . . call +412 e8/call clear-stream/disp32 +413 # . . discard args +414 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +415 # . clear-stream(_test-buffered-file+4) +416 # . . push args +417 b8/copy-to-EAX _test-buffered-file/imm32 +418 05/add-to-EAX 4/imm32 +419 50/push-EAX +420 # . . call +421 e8/call clear-stream/disp32 +422 # . . discard args +423 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +424 # . clear-stream(_test-output-stream) +425 # . . push args +426 68/push _test-output-stream/imm32 +427 # . . call +428 e8/call clear-stream/disp32 +429 # . . discard args +430 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +431 # . clear-stream(_test-error-stream) +432 # . . push args +433 68/push _test-error-stream/imm32 +434 # . . call +435 e8/call clear-stream/disp32 +436 # . . discard args +437 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +438 # initialize 'in' +439 # . write(_test-stream, "3") +440 # . . push args +441 68/push "3"/imm32 +442 68/push _test-stream/imm32 +443 # . . call +444 e8/call write/disp32 +445 # . . discard args +446 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +447 # initialize exit-descriptor 'ed' for the call to 'get-num' below +448 # . var ed/EAX : (address exit-descriptor) +449 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +450 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +451 # . tailor-exit-descriptor(ed, 16) +452 # . . push args +453 68/push 0x10/imm32/nbytes-of-args-for-get-num +454 50/push-EAX/ed +455 # . . call +456 e8/call tailor-exit-descriptor/disp32 +457 # . . discard args +458 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +459 # *don't* prime the pump +460 # get-num(in, out, err, ed) +461 # . . push args +462 50/push-EAX/ed +463 68/push _test-error-stream/imm32 +464 68/push _test-output-stream/imm32 +465 68/push _test-buffered-file/imm32 +466 # . . call +467 e8/call get-num/disp32 +468 # registers except ESP may be clobbered at this point +469 # . . discard args +470 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +471 # check that get-num tried to call exit(1) +472 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 +473 # . . push args +474 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 +475 68/push 2/imm32 +476 # . . push ed->value +477 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +478 # . . call +479 e8/call check-ints-equal/disp32 +480 # . . discard args +481 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +482 # . reclaim locals +483 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +484 5d/pop-to-EBP +485 c3/return +486 +487 test-get-num-reads-multiple-digits: +488 # - check that get-num returns all initial digits until it encounters a non-digit +489 # This test uses exit-descriptors. Use EBP for setting up local variables. +490 55/push-EBP +491 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +492 # clear all streams +493 # . clear-stream(_test-stream) +494 # . . push args +495 68/push _test-stream/imm32 +496 # . . call +497 e8/call clear-stream/disp32 +498 # . . discard args +499 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +500 # . clear-stream(_test-buffered-file+4) +501 # . . push args +502 b8/copy-to-EAX _test-buffered-file/imm32 +503 05/add-to-EAX 4/imm32 +504 50/push-EAX +505 # . . call +506 e8/call clear-stream/disp32 +507 # . . discard args +508 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +509 # . clear-stream(_test-output-stream) +510 # . . push args +511 68/push _test-output-stream/imm32 +512 # . . call +513 e8/call clear-stream/disp32 +514 # . . discard args +515 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +516 # . clear-stream(_test-error-stream) +517 # . . push args +518 68/push _test-error-stream/imm32 +519 # . . call +520 e8/call clear-stream/disp32 +521 # . . discard args +522 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +523 # initialize 'in' +524 # . write(_test-stream, "3456 x") +525 # . . push args +526 68/push "3456"/imm32 +527 68/push _test-stream/imm32 +528 # . . call +529 e8/call write/disp32 +530 # . . discard args +531 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +532 # initialize exit-descriptor 'ed' for the call to 'get-num' below +533 # . var ed/EAX : (address exit-descriptor) +534 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +535 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +536 # . tailor-exit-descriptor(ed, 16) +537 # . . push args +538 68/push 0x10/imm32/nbytes-of-args-for-get-num +539 50/push-EAX/ed +540 # . . call +541 e8/call tailor-exit-descriptor/disp32 +542 # . . discard args +543 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +544 # prime the pump +545 # . get-char(_test-buffered-file) +546 # . . push args +547 68/push _test-buffered-file/imm32 +548 # . . call +549 e8/call get-char/disp32 +550 # . . discard args +551 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +552 # get-num(in, out, err, ed) +553 # . . push args +554 50/push-EAX/ed +555 68/push _test-error-stream/imm32 +556 68/push _test-output-stream/imm32 +557 68/push _test-buffered-file/imm32 +558 # . . call +559 e8/call get-num/disp32 +560 # registers except ESP may be clobbered at this point +561 # . . discard args +562 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +563 # check-ints-equal(*_test-output-stream->data, '3456', msg) +564 # . . push args +565 68/push "F - test-get-num-reads-multiple-digits"/imm32 +566 68/push 0x36353433/imm32 +567 b8/copy-to-EAX _test-output-stream/imm32 +568 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +569 # . . call +570 e8/call check-ints-equal/disp32 +571 # . . discard args +572 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +573 # . reclaim locals +574 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +575 5d/pop-to-EBP +576 c3/return +577 +578 test-get-num-reads-multiple-digits-followed-by-nondigit: +579 # - check that get-num returns all initial digits until it encounters a non-digit +580 # This test uses exit-descriptors. Use EBP for setting up local variables. +581 55/push-EBP +582 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +583 # clear all streams +584 # . clear-stream(_test-stream) +585 # . . push args +586 68/push _test-stream/imm32 +587 # . . call +588 e8/call clear-stream/disp32 +589 # . . discard args +590 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +591 # . clear-stream(_test-buffered-file+4) +592 # . . push args +593 b8/copy-to-EAX _test-buffered-file/imm32 +594 05/add-to-EAX 4/imm32 +595 50/push-EAX +596 # . . call +597 e8/call clear-stream/disp32 +598 # . . discard args +599 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +600 # . clear-stream(_test-output-stream) +601 # . . push args +602 68/push _test-output-stream/imm32 +603 # . . call +604 e8/call clear-stream/disp32 +605 # . . discard args +606 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +607 # . clear-stream(_test-error-stream) +608 # . . push args +609 68/push _test-error-stream/imm32 +610 # . . call +611 e8/call clear-stream/disp32 +612 # . . discard args +613 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +614 # initialize 'in' +615 # . write(_test-stream, "3456 x") +616 # . . push args +617 68/push "3456 x"/imm32 +618 68/push _test-stream/imm32 +619 # . . call +620 e8/call write/disp32 +621 # . . discard args +622 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +623 # initialize exit-descriptor 'ed' for the call to 'get-num' below +624 # . var ed/EAX : (address exit-descriptor) +625 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +626 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +627 # . tailor-exit-descriptor(ed, 16) +628 # . . push args +629 68/push 0x10/imm32/nbytes-of-args-for-get-num +630 50/push-EAX/ed +631 # . . call +632 e8/call tailor-exit-descriptor/disp32 +633 # . . discard args +634 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +635 # prime the pump +636 # . get-char(_test-buffered-file) +637 # . . push args +638 68/push _test-buffered-file/imm32 +639 # . . call +640 e8/call get-char/disp32 +641 # . . discard args +642 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +643 # get-num(in, out, err, ed) +644 # . . push args +645 50/push-EAX/ed +646 68/push _test-error-stream/imm32 +647 68/push _test-output-stream/imm32 +648 68/push _test-buffered-file/imm32 +649 # . . call +650 e8/call get-num/disp32 +651 # registers except ESP may be clobbered at this point +652 # . . discard args +653 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +654 # check-ints-equal(*_test-output-stream->data, '3456', msg) +655 # . . push args +656 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 +657 68/push 0x36353433/imm32 +658 b8/copy-to-EAX _test-output-stream/imm32 +659 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +660 # . . call +661 e8/call check-ints-equal/disp32 +662 # . . discard args +663 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +664 # . reclaim locals +665 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +666 5d/pop-to-EBP +667 c3/return +668 +669 ## helpers +670 +671 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) +672 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> +673 # . prolog +674 55/push-EBP +675 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +676 # write(f, "Error: ") +677 # . . push args +678 68/push "Error: "/imm32 +679 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +680 # . . call +681 e8/call write/disp32 +682 # . . discard args +683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +684 # write(f, s) +685 # . . push args +686 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +687 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +688 # . . call +689 e8/call write/disp32 +690 # . . discard args +691 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +692 # write(f, " expected\n") +693 # . . push args +694 68/push " expected\n"/imm32 +695 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +696 # . . call +697 e8/call write/disp32 +698 # . . discard args +699 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +700 # stop(ed, 1) +701 # . . push args +702 68/push 1/imm32 +703 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +704 # . . call +705 e8/call stop/disp32 +706 # should never get past this point +707 $expected:dead-end: +708 # . epilog +709 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +710 5d/pop-to-EBP +711 c3/return +712 +713 # read a byte from 'f', and save it in 'Look' +714 get-char: # f : (address buffered-file) -> <void> +715 # . prolog +716 55/push-EBP +717 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +718 # . save registers +719 50/push-EAX +720 # EAX = read-byte-buffered(f) +721 # . . push args +722 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +723 # . . call +724 e8/call read-byte-buffered/disp32 +725 # . . discard args +726 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +727 # save EAX to Look +728 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look +729 $get-char:end: +730 # . restore registers +731 58/pop-to-EAX +732 # . epilog +733 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +734 5d/pop-to-EBP +735 c3/return +736 +737 is-digit?: # c : int -> EAX : boolean +738 # . prolog +739 55/push-EBP +740 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +741 # EAX = false +742 b8/copy-to-EAX 0/imm32 +743 # if (c < '0') return false +744 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) +745 7c/jump-if-lesser $is-digit?:end/disp8 +746 # if (c > '9') return false +747 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) +748 7f/jump-if-greater $is-digit?:end/disp8 +749 # otherwise return true +750 b8/copy-to-EAX 1/imm32 +751 $is-digit?:end: +752 # . epilog +753 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +754 5d/pop-to-EBP +755 c3/return 756 -757 _test-output-stream: -758 # current write index -759 0/imm32 -760 # current read index -761 0/imm32 -762 # length -763 8/imm32 -764 # data -765 00 00 00 00 00 00 00 00 # 8 bytes -766 -767 _test-error-stream: -768 # current write index -769 0/imm32 -770 # current read index -771 0/imm32 -772 # length -773 0x40/imm32 -774 # data (4 lines x 16 bytes/line) -775 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -776 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -777 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -778 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -779 -780 # . . vim:nowrap:textwidth=0 +757 == data +758 +759 Look: # (char with some extra padding) +760 0/imm32 +761 +762 _test-output-stream: +763 # current write index +764 0/imm32 +765 # current read index +766 0/imm32 +767 # length +768 8/imm32 +769 # data +770 00 00 00 00 00 00 00 00 # 8 bytes +771 +772 _test-error-stream: +773 # current write index +774 0/imm32 +775 # current read index +776 0/imm32 +777 # length +778 0x40/imm32 +779 # data (4 lines x 16 bytes/line) +780 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +781 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +782 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +783 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +784 +785 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/dquotes.subx.html b/html/subx/apps/dquotes.subx.html index 690d3b10..682ab687 100644 --- a/html/subx/apps/dquotes.subx.html +++ b/html/subx/apps/dquotes.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/dquotes.subx - - + + @@ -14,19 +14,18 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } .Folded { color: #080808; background-color: #949494; } +.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.SpecialChar { color: #d70000; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -43,7 +42,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -87,1230 +86,1230 @@ if ('onhashchange' in window) { 23 # initialize heap 24 # . Heap = new-segment(64KB) 25 # . . push args - 26 68/push Heap/imm32 + 26 68/push Heap/imm32 27 68/push 0x10000/imm32/64KB 28 # . . call 29 e8/call new-segment/disp32 30 # . . discard args 31 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 32 - 33 # for debugging: run a single test - 34 #? e8/call test-string-length-at-start-of-slice-escaped/disp32 - 35 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 36 #? eb/jump $main:end/disp8 - 37 - 38 # run tests if necessary, convert stdin if not - 39 # . prolog - 40 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 41 # - if argc > 1 and argv[1] == "test", then return run_tests() - 42 # . argc > 1 - 43 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 44 7e/jump-if-lesser-or-equal $run-main/disp8 - 45 # . argv[1] == "test" - 46 # . . push args - 47 68/push "test"/imm32 - 48 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 49 # . . call - 50 e8/call kernel-string-equal?/disp32 - 51 # . . discard args - 52 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 53 # . check result - 54 3d/compare-EAX-and 1/imm32 - 55 75/jump-if-not-equal $run-main/disp8 - 56 # . run-tests() - 57 e8/call run-tests/disp32 - 58 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 59 eb/jump $main:end/disp8 - 60 $run-main: - 61 # - otherwise convert stdin - 62 # var ed/EAX : exit-descriptor - 63 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 64 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - 65 # configure ed to really exit() - 66 # . ed->target = 0 - 67 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 68 # return convert(Stdin, 1/stdout, 2/stderr, ed) - 69 # . . push args - 70 50/push-EAX/ed - 71 68/push Stderr/imm32 - 72 68/push Stdout/imm32 - 73 68/push Stdin/imm32 - 74 # . . call - 75 e8/call convert/disp32 - 76 # . . discard args - 77 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 78 # . syscall(exit, 0) - 79 bb/copy-to-EBX 0/imm32 - 80 $main:end: - 81 b8/copy-to-EAX 1/imm32/exit - 82 cd/syscall 0x80/imm8 - 83 - 84 # conceptual hierarchy within a line: - 85 # line = words separated by ' ', maybe followed by comment starting with '#' - 86 # word = datum until '/', then 0 or more metadata separated by '/' - 87 - 88 convert: # in : (address buffered-file), out : (address buffered-file) -> <void> - 89 # pseudocode: - 90 # var line = new-stream(512, 1) - 91 # var new-data-segment = new-stream(Heap, Segment-size, 1) - 92 # write-stream(new-data-segment, "== data\n") - 93 # while true - 94 # clear-stream(line) - 95 # read-line-buffered(in, line) - 96 # if (line->write == 0) break # end of file - 97 # while true - 98 # var word-slice = next-word(line) - 99 # if slice-empty?(word-slice) # end of line - 100 # break - 101 # if slice-starts-with?(word-slice, "#") # comment - 102 # continue - 103 # if slice-starts-with?(word-slice, '"') # string literal <== what we're here for - 104 # process-string-literal(word-slice, out, new-data-segment) - 105 # else - 106 # write-slice-buffered(out, word-slice) - 107 # write(out, " ") - 108 # write(out, "\n\n") - 109 # write-stream-data(out, new-data-segment) - 110 # flush(out) - 111 # - 112 # . prolog - 113 55/push-EBP - 114 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 115 # . save registers - 116 50/push-EAX - 117 51/push-ECX - 118 52/push-EDX - 119 53/push-EBX - 120 56/push-ESI - 121 57/push-EDI - 122 # var line/ECX : (address stream byte) = stream(512) - 123 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP - 124 68/push 0x200/imm32/length - 125 68/push 0/imm32/read - 126 68/push 0/imm32/write - 127 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 128 # var word-slice/EDX = {0, 0} - 129 68/push 0/imm32/end - 130 68/push 0/imm32/curr - 131 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - 132 # new-data-segment/EDI = new-stream(Heap, Segment-size, 1) - 133 # . EAX = new-stream(Heap, Segment-size, 1) - 134 # . . push args - 135 68/push 1/imm32 - 136 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Segment-size/disp32 # push *Segment-size - 137 68/push Heap/imm32 - 138 # . . call - 139 e8/call new-stream/disp32 - 140 # . . discard args - 141 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 142 # . EDI = EAX - 143 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI - 144 # write(new-data-segment, "== data\n") - 145 # . . push args - 146 68/push "== data\n"/imm32 - 147 57/push-EDI - 148 # . . call - 149 e8/call write/disp32 - 150 # . . discard args - 151 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 152 $convert:line-loop: - 153 # clear-stream(line) - 154 # . . push args - 155 51/push-ECX - 156 # . . call - 157 e8/call clear-stream/disp32 - 158 # . . discard args - 159 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 160 # read-line-buffered(in, line) - 161 # . . push args - 162 51/push-ECX - 163 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 164 # . . call - 165 e8/call read-line-buffered/disp32 - 166 # . . discard args - 167 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 168 $convert:check0: - 169 # if (line->write == 0) break - 170 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX - 171 0f 84/jump-if-equal $convert:break/disp32 - 172 $convert:word-loop: - 173 # next-word(line, word-slice) - 174 # . . push args - 175 52/push-EDX - 176 51/push-ECX - 177 # . . call - 178 e8/call next-word/disp32 - 179 # . . discard args - 180 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 181 $convert:check1: - 182 # if (slice-empty?(word-slice)) break - 183 # . EAX = slice-empty?(word-slice) - 184 # . . push args - 185 52/push-EDX - 186 # . . call - 187 e8/call slice-empty?/disp32 - 188 # . . discard args - 189 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 190 # . if (EAX != 0) break - 191 3d/compare-EAX-and 0/imm32 - 192 0f 85/jump-if-not-equal $convert:next-line/disp32 - 193 $convert:check-for-comment: - 194 # if (slice-starts-with?(word-slice, "#")) continue - 195 # . start/ESI = word-slice->start - 196 8b/copy 0/mod/indirect 2/rm32/EDX . . . 6/r32/ESI . . # copy *EDX to ESI - 197 # . c/EAX = *start - 198 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 199 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - 200 # . if (EAX == '#') continue - 201 3d/compare-EAX-and 0x23/imm32/hash - 202 74/jump-if-equal $convert:word-loop/disp8 - 203 $convert:check-for-string-literal: - 204 3d/compare-EAX-and 0x22/imm32/hash - 205 75/jump-if-not-equal $convert:regular-word/disp8 - 206 $convert:string-literal: - 207 # process-string-literal(word-slice, out, new-data-segment) - 208 # . . push args - 209 57/push-EDI - 210 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 211 52/push-EDX - 212 # . . call - 213 e8/call process-string-literal/disp32 - 214 # . . discard args - 215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 216 # continue - 217 eb/jump $convert:next-word/disp8 - 218 $convert:regular-word: - 219 # write-slice-buffered(out, word-slice) - 220 # . . push args - 221 52/push-EDX - 222 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 223 # . . call - 224 e8/call write-slice-buffered/disp32 - 225 # . . discard args - 226 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 227 # fall through - 228 $convert:next-word: - 229 # write-buffered(out, " ") - 230 # . . push args - 231 68/push " "/imm32 - 232 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 233 # . . call - 234 e8/call write-buffered/disp32 - 235 # . . discard args - 236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 237 # loop - 238 eb/jump $convert:word-loop/disp8 - 239 $convert:next-line: - 240 # write-buffered(out, "\n") - 241 # . . push args - 242 68/push Newline/imm32 - 243 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 244 # . . call - 245 e8/call write-buffered/disp32 - 246 # . . discard args - 247 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 248 # loop - 249 e9/jump $convert:line-loop/disp32 - 250 $convert:break: - 251 # write-stream-data(out, new-data-segment) - 252 # . . push args - 253 57/push-EDI - 254 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 255 # . . call - 256 e8/call write-stream-data/disp32 - 257 # . . discard args - 258 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 259 # flush(out) - 260 # . . push args - 261 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 262 # . . call - 263 e8/call flush/disp32 - 264 # . . discard args - 265 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 266 $convert:end: - 267 # . reclaim locals - 268 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP - 269 # . restore registers - 270 5f/pop-to-EDI - 271 5e/pop-to-ESI - 272 5b/pop-to-EBX - 273 5a/pop-to-EDX - 274 59/pop-to-ECX - 275 58/pop-to-EAX - 276 # . epilog - 277 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 278 5d/pop-to-EBP - 279 c3/return - 280 - 281 # Write out 'string-literal' in a new format to 'out-segment', assign it a new - 282 # label, and write the new label out to 'out'. - 283 process-string-literal: # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream) - 284 # pseudocode: - 285 # print(out-segment, "_string#{Next-string-literal}:\n") - 286 # emit-string-literal-data(out-segment, string-literal) - 287 # print(out, "_string#{Next-string-literal}") - 288 # emit-metadata(out, string-literal) - 289 # ++ *Next-string-literal - 290 # - 291 # . prolog - 292 55/push-EBP - 293 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 294 # . save registers - 295 51/push-ECX - 296 # var int32-stream/ECX = stream(10) # number of decimal digits a 32-bit number can have - 297 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa/imm32 # subtract from ESP - 298 68/push 0xa/imm32/decimal-digits-in-32bit-number - 299 68/push 0/imm32/read - 300 68/push 0/imm32/write - 301 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 302 # print(out-segment, "_string#{Next-string-literal}:\n") - 303 # . write(out-segment, "_string") - 304 # . . push args - 305 68/push "_string"/imm32 - 306 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 307 # . . call - 308 e8/call write/disp32 - 309 # . . discard args - 310 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 311 # . print-int32-decimal(out-segment, *Next-string-literal) - 312 # . . push args - 313 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal - 314 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 315 # . . call - 316 e8/call print-int32-decimal/disp32 - 317 # . . discard args - 318 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 319 # . write(out-segment, ":\n") - 320 # . . push args - 321 68/push ":\n"/imm32 - 322 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 323 # . . call - 324 e8/call write/disp32 - 325 # . . discard args - 326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 327 # emit-string-literal-data(out-segment, string-literal) - 328 # . . push args - 329 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x8/disp8 . # push *(EBP+8) - 330 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 331 # . . call - 332 e8/call emit-string-literal-data/disp32 - 333 # . . discard args - 334 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 335 # write(out-segment, "\n") - 336 # . . push args - 337 68/push Newline/imm32 - 338 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 339 # . . call - 340 e8/call write/disp32 - 341 # . . discard args - 342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 343 # print(out, "_string#{Next-string-literal}") - 344 # . write-buffered(out, "_string") - 345 # . . push args - 346 68/push "_string"/imm32 - 347 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 348 # . . call - 349 e8/call write-buffered/disp32 - 350 # . . discard args - 351 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 352 # . print-int32-decimal(int32-stream, *Next-string-literal) - 353 # . . push args - 354 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal - 355 51/push-ECX - 356 # . . call - 357 e8/call print-int32-decimal/disp32 - 358 # . . discard args - 359 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 360 # . write-stream-data(out, int32-stream) - 361 # . . push args - 362 51/push-ECX - 363 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 364 # . . call - 365 e8/call write-stream-data/disp32 - 366 # . . discard args - 367 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 368 # emit-metadata(out, string-literal) - 369 # . . push args - 370 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 371 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 372 # . . call - 373 e8/call emit-metadata/disp32 - 374 # . . discard args - 375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 376 # ++ *Next-string-literal - 377 ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # increment *Num-test-failures - 378 $process-string-literal:end: - 379 # . reclaim locals - 380 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x16/imm32 # add to ESP - 381 # . restore registers - 382 59/pop-to-ECX - 383 # . epilog - 384 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 385 5d/pop-to-EBP - 386 c3/return - 387 - 388 test-convert-is-idempotent-by-default: - 389 # . prolog - 390 55/push-EBP - 391 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 392 # setup - 393 # . clear-stream(_test-input-stream) - 394 # . . push args - 395 68/push _test-input-stream/imm32 - 396 # . . call - 397 e8/call clear-stream/disp32 - 398 # . . discard args - 399 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 400 # . clear-stream(_test-input-buffered-file+4) - 401 # . . push args - 402 b8/copy-to-EAX _test-input-buffered-file/imm32 - 403 05/add-to-EAX 4/imm32 - 404 50/push-EAX - 405 # . . call - 406 e8/call clear-stream/disp32 - 407 # . . discard args - 408 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 409 # . clear-stream(_test-output-stream) - 410 # . . push args - 411 68/push _test-output-stream/imm32 - 412 # . . call - 413 e8/call clear-stream/disp32 - 414 # . . discard args - 415 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 416 # . clear-stream(_test-output-buffered-file+4) - 417 # . . push args - 418 b8/copy-to-EAX _test-output-buffered-file/imm32 - 419 05/add-to-EAX 4/imm32 - 420 50/push-EAX - 421 # . . call - 422 e8/call clear-stream/disp32 - 423 # . . discard args - 424 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 425 # initialize input (meta comments in parens) - 426 # # comment 1 - 427 # # comment 2 indented - 428 # == code 0x1 (new segment) - 429 # # comment 3 inside a segment - 430 # 1 - 431 # (empty line) - 432 # 2 3 # comment 4 inline with other contents - 433 # == data 0x2 (new segment) - 434 # 4 5/imm32 - 435 # . write(_test-input-stream, "# comment 1\n") - 436 # . . push args - 437 68/push "# comment 1\n"/imm32 - 438 68/push _test-input-stream/imm32 - 439 # . . call - 440 e8/call write/disp32 - 441 # . . discard args - 442 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 443 # . write(_test-input-stream, " # comment 2 indented\n") - 444 # . . push args - 445 68/push " # comment 2 indented\n"/imm32 - 446 68/push _test-input-stream/imm32 - 447 # . . call - 448 e8/call write/disp32 - 449 # . . discard args - 450 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 451 # . write(_test-input-stream, "== code 0x1\n") - 452 # . . push args - 453 68/push "== code 0x1\n"/imm32 - 454 68/push _test-input-stream/imm32 - 455 # . . call - 456 e8/call write/disp32 - 457 # . . discard args - 458 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 459 # . write(_test-input-stream, "# comment 3 inside a segment\n") - 460 # . . push args - 461 68/push "# comment 3 inside a segment\n"/imm32 - 462 68/push _test-input-stream/imm32 - 463 # . . call - 464 e8/call write/disp32 - 465 # . . discard args - 466 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 467 # . write(_test-input-stream, "1\n") - 468 # . . push args - 469 68/push "1\n"/imm32 - 470 68/push _test-input-stream/imm32 - 471 # . . call - 472 e8/call write/disp32 - 473 # . . discard args - 474 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 475 # . write(_test-input-stream, "\n") # empty line - 476 # . . push args - 477 68/push "\n"/imm32 - 478 68/push _test-input-stream/imm32 - 479 # . . call - 480 e8/call write/disp32 - 481 # . . discard args - 482 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 483 # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n") - 484 # . . push args - 485 68/push "2 3 # comment 4 inline with other contents\n"/imm32 - 486 68/push _test-input-stream/imm32 - 487 # . . call - 488 e8/call write/disp32 - 489 # . . discard args - 490 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 491 # . write(_test-input-stream, "== data 0x2\n") - 492 # . . push args - 493 68/push "== data 0x2\n"/imm32 - 494 68/push _test-input-stream/imm32 - 495 # . . call - 496 e8/call write/disp32 - 497 # . . discard args - 498 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 499 # . write(_test-input-stream, "4 5/imm32\n") - 500 # . . push args - 501 68/push "4 5/imm32\n"/imm32 - 502 68/push _test-input-stream/imm32 - 503 # . . call - 504 e8/call write/disp32 - 505 # . . discard args - 506 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 507 # convert(_test-input-buffered-file, _test-output-buffered-file) - 508 # . . push args - 509 68/push _test-output-buffered-file/imm32 - 510 68/push _test-input-buffered-file/imm32 - 511 # . . call - 512 e8/call convert/disp32 - 513 # . . discard args - 514 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 515 # . flush(_test-output-buffered-file) - 516 # . . push args - 517 68/push _test-output-buffered-file/imm32 - 518 # . . call - 519 e8/call flush/disp32 - 520 # . . discard args - 521 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 522 # check output + 33 # run tests if necessary, convert stdin if not + 34 # . prolog + 35 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 36 # - if argc > 1 and argv[1] == "test", then return run_tests() + 37 # . argc > 1 + 38 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP + 39 7e/jump-if-lesser-or-equal $run-main/disp8 + 40 # . argv[1] == "test" + 41 # . . push args + 42 68/push "test"/imm32 + 43 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 44 # . . call + 45 e8/call kernel-string-equal?/disp32 + 46 # . . discard args + 47 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 48 # . check result + 49 3d/compare-EAX-and 1/imm32 + 50 75/jump-if-not-equal $run-main/disp8 + 51 # . run-tests() + 52 e8/call run-tests/disp32 + 53 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 54 eb/jump $main:end/disp8 + 55 $run-main: + 56 # - otherwise convert stdin + 57 # var ed/EAX : exit-descriptor + 58 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 59 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX + 60 # configure ed to really exit() + 61 # . ed->target = 0 + 62 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 63 # return convert(Stdin, 1/stdout, 2/stderr, ed) + 64 # . . push args + 65 50/push-EAX/ed + 66 68/push Stderr/imm32 + 67 68/push Stdout/imm32 + 68 68/push Stdin/imm32 + 69 # . . call + 70 e8/call convert/disp32 + 71 # . . discard args + 72 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 73 # . syscall(exit, 0) + 74 bb/copy-to-EBX 0/imm32 + 75 $main:end: + 76 b8/copy-to-EAX 1/imm32/exit + 77 cd/syscall 0x80/imm8 + 78 + 79 # conceptual hierarchy within a line: + 80 # line = words separated by ' ', maybe followed by comment starting with '#' + 81 # word = datum until '/', then 0 or more metadata separated by '/' + 82 + 83 convert: # in : (address buffered-file), out : (address buffered-file) -> <void> + 84 # pseudocode: + 85 # var line = new-stream(512, 1) + 86 # var new-data-segment = new-stream(Heap, Segment-size, 1) + 87 # write-stream(new-data-segment, "== data\n") + 88 # while true + 89 # clear-stream(line) + 90 # read-line-buffered(in, line) + 91 # if (line->write == 0) break # end of file + 92 # while true + 93 # var word-slice = next-word-or-string(line) + 94 # if slice-empty?(word-slice) # end of line + 95 # break + 96 # if slice-starts-with?(word-slice, "#") # comment + 97 # continue + 98 # if slice-starts-with?(word-slice, '"') # string literal <== what we're here for + 99 # process-string-literal(word-slice, out, new-data-segment) + 100 # else + 101 # write-slice-buffered(out, word-slice) + 102 # write(out, " ") + 103 # write(out, "\n\n") + 104 # write-stream-data(out, new-data-segment) + 105 # flush(out) + 106 # + 107 # . prolog + 108 55/push-EBP + 109 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 110 # . save registers + 111 50/push-EAX + 112 51/push-ECX + 113 52/push-EDX + 114 53/push-EBX + 115 56/push-ESI + 116 57/push-EDI + 117 # var line/ECX : (address stream byte) = stream(512) + 118 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP + 119 68/push 0x200/imm32/length + 120 68/push 0/imm32/read + 121 68/push 0/imm32/write + 122 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 123 # var word-slice/EDX = {0, 0} + 124 68/push 0/imm32/end + 125 68/push 0/imm32/start + 126 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX + 127 # new-data-segment/EDI = new-stream(Heap, Segment-size, 1) + 128 # . EAX = new-stream(Heap, Segment-size, 1) + 129 # . . push args + 130 68/push 1/imm32 + 131 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Segment-size/disp32 # push *Segment-size + 132 68/push Heap/imm32 + 133 # . . call + 134 e8/call new-stream/disp32 + 135 # . . discard args + 136 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 137 # . EDI = EAX + 138 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI + 139 # write(new-data-segment, "== data\n") + 140 # . . push args + 141 68/push "== data\n"/imm32 + 142 57/push-EDI + 143 # . . call + 144 e8/call write/disp32 + 145 # . . discard args + 146 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 147 $convert:line-loop: + 148 # clear-stream(line) + 149 # . . push args + 150 51/push-ECX + 151 # . . call + 152 e8/call clear-stream/disp32 + 153 # . . discard args + 154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 155 # read-line-buffered(in, line) + 156 # . . push args + 157 51/push-ECX + 158 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 159 # . . call + 160 e8/call read-line-buffered/disp32 + 161 # . . discard args + 162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 163 $convert:check0: + 164 # if (line->write == 0) break + 165 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX + 166 0f 84/jump-if-equal $convert:break/disp32 + 167 $convert:word-loop: + 168 # next-word-or-string(line, word-slice) + 169 # . . push args + 170 52/push-EDX + 171 51/push-ECX + 172 # . . call + 173 e8/call next-word-or-string/disp32 + 174 # . . discard args + 175 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 176 $convert:check1: + 177 # if (slice-empty?(word-slice)) break + 178 # . EAX = slice-empty?(word-slice) + 179 # . . push args + 180 52/push-EDX + 181 # . . call + 182 e8/call slice-empty?/disp32 + 183 # . . discard args + 184 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 185 # . if (EAX != 0) break + 186 3d/compare-EAX-and 0/imm32 + 187 0f 85/jump-if-not-equal $convert:next-line/disp32 + 188 $convert:check-for-comment: + 189 # if (slice-starts-with?(word-slice, "#")) continue + 190 # . start/ESI = word-slice->start + 191 8b/copy 0/mod/indirect 2/rm32/EDX . . . 6/r32/ESI . . # copy *EDX to ESI + 192 # . c/EAX = *start + 193 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 194 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL + 195 # . if (EAX == '#') continue + 196 3d/compare-EAX-and 0x23/imm32/hash + 197 74/jump-if-equal $convert:word-loop/disp8 + 198 $convert:check-for-string-literal: + 199 3d/compare-EAX-and 0x22/imm32/hash + 200 75/jump-if-not-equal $convert:regular-word/disp8 + 201 $convert:string-literal: + 202 # process-string-literal(word-slice, out, new-data-segment) + 203 # . . push args + 204 57/push-EDI + 205 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 206 52/push-EDX + 207 # . . call + 208 e8/call process-string-literal/disp32 + 209 # . . discard args + 210 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 211 # continue + 212 eb/jump $convert:next-word/disp8 + 213 $convert:regular-word: + 214 # write-slice-buffered(out, word-slice) + 215 # . . push args + 216 52/push-EDX + 217 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 218 # . . call + 219 e8/call write-slice-buffered/disp32 + 220 # . . discard args + 221 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 222 # fall through + 223 $convert:next-word: + 224 # write-buffered(out, " ") + 225 # . . push args + 226 68/push " "/imm32 + 227 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 228 # . . call + 229 e8/call write-buffered/disp32 + 230 # . . discard args + 231 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 232 # loop + 233 eb/jump $convert:word-loop/disp8 + 234 $convert:next-line: + 235 # write-buffered(out, "\n") + 236 # . . push args + 237 68/push Newline/imm32 + 238 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 239 # . . call + 240 e8/call write-buffered/disp32 + 241 # . . discard args + 242 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 243 # loop + 244 e9/jump $convert:line-loop/disp32 + 245 $convert:break: + 246 # write-stream-data(out, new-data-segment) + 247 # . . push args + 248 57/push-EDI + 249 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 250 # . . call + 251 e8/call write-stream-data/disp32 + 252 # . . discard args + 253 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 254 # flush(out) + 255 # . . push args + 256 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 257 # . . call + 258 e8/call flush/disp32 + 259 # . . discard args + 260 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 261 $convert:end: + 262 # . reclaim locals + 263 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP + 264 # . restore registers + 265 5f/pop-to-EDI + 266 5e/pop-to-ESI + 267 5b/pop-to-EBX + 268 5a/pop-to-EDX + 269 59/pop-to-ECX + 270 58/pop-to-EAX + 271 # . epilog + 272 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 273 5d/pop-to-EBP + 274 c3/return + 275 + 276 # Write out 'string-literal' in a new format to 'out-segment', assign it a new + 277 # label, and write the new label out to 'out'. + 278 process-string-literal: # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream) + 279 # pseudocode: + 280 # print(out-segment, "_string#{Next-string-literal}:\n") + 281 # emit-string-literal-data(out-segment, string-literal) + 282 # print(out, "_string#{Next-string-literal}") + 283 # emit-metadata(out, string-literal) + 284 # ++ *Next-string-literal + 285 # + 286 # . prolog + 287 55/push-EBP + 288 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 289 # . save registers + 290 51/push-ECX + 291 # var int32-stream/ECX = stream(10) # number of decimal digits a 32-bit number can have + 292 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa/imm32 # subtract from ESP + 293 68/push 0xa/imm32/decimal-digits-in-32bit-number + 294 68/push 0/imm32/read + 295 68/push 0/imm32/write + 296 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 297 # print(out-segment, "_string#{Next-string-literal}:\n") + 298 # . write(out-segment, "_string") + 299 # . . push args + 300 68/push "_string"/imm32 + 301 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 302 # . . call + 303 e8/call write/disp32 + 304 # . . discard args + 305 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 306 # . print-int32-decimal(out-segment, *Next-string-literal) + 307 # . . push args + 308 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal + 309 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 310 # . . call + 311 e8/call print-int32-decimal/disp32 + 312 # . . discard args + 313 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 314 # . write(out-segment, ":\n") + 315 # . . push args + 316 68/push ":\n"/imm32 + 317 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 318 # . . call + 319 e8/call write/disp32 + 320 # . . discard args + 321 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 322 # emit-string-literal-data(out-segment, string-literal) + 323 # . . push args + 324 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x8/disp8 . # push *(EBP+8) + 325 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 326 # . . call + 327 e8/call emit-string-literal-data/disp32 + 328 # . . discard args + 329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 330 # write(out-segment, "\n") + 331 # . . push args + 332 68/push Newline/imm32 + 333 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 334 # . . call + 335 e8/call write/disp32 + 336 # . . discard args + 337 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 338 # print(out, "_string#{Next-string-literal}") + 339 # . write-buffered(out, "_string") + 340 # . . push args + 341 68/push "_string"/imm32 + 342 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 343 # . . call + 344 e8/call write-buffered/disp32 + 345 # . . discard args + 346 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 347 # . print-int32-decimal(int32-stream, *Next-string-literal) + 348 # . . push args + 349 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal + 350 51/push-ECX + 351 # . . call + 352 e8/call print-int32-decimal/disp32 + 353 # . . discard args + 354 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 355 # . write-stream-data(out, int32-stream) + 356 # . . push args + 357 51/push-ECX + 358 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 359 # . . call + 360 e8/call write-stream-data/disp32 + 361 # . . discard args + 362 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 363 # emit-metadata(out, string-literal) + 364 # . . push args + 365 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 366 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 367 # . . call + 368 e8/call emit-metadata/disp32 + 369 # . . discard args + 370 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 371 # ++ *Next-string-literal + 372 ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # increment *Num-test-failures + 373 $process-string-literal:end: + 374 # . reclaim locals + 375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x16/imm32 # add to ESP + 376 # . restore registers + 377 59/pop-to-ECX + 378 # . epilog + 379 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 380 5d/pop-to-EBP + 381 c3/return + 382 + 383 test-convert-is-idempotent-by-default: + 384 # . prolog + 385 55/push-EBP + 386 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 387 # setup + 388 # . clear-stream(_test-input-stream) + 389 # . . push args + 390 68/push _test-input-stream/imm32 + 391 # . . call + 392 e8/call clear-stream/disp32 + 393 # . . discard args + 394 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 395 # . clear-stream(_test-input-buffered-file+4) + 396 # . . push args + 397 b8/copy-to-EAX _test-input-buffered-file/imm32 + 398 05/add-to-EAX 4/imm32 + 399 50/push-EAX + 400 # . . call + 401 e8/call clear-stream/disp32 + 402 # . . discard args + 403 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 404 # . clear-stream(_test-output-stream) + 405 # . . push args + 406 68/push _test-output-stream/imm32 + 407 # . . call + 408 e8/call clear-stream/disp32 + 409 # . . discard args + 410 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 411 # . clear-stream(_test-output-buffered-file+4) + 412 # . . push args + 413 b8/copy-to-EAX _test-output-buffered-file/imm32 + 414 05/add-to-EAX 4/imm32 + 415 50/push-EAX + 416 # . . call + 417 e8/call clear-stream/disp32 + 418 # . . discard args + 419 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 420 # initialize input (meta comments in parens) + 421 # # comment 1 + 422 # # comment 2 indented + 423 # == code 0x1 (new segment) + 424 # # comment 3 inside a segment + 425 # 1 + 426 # (empty line) + 427 # 2 3 # comment 4 inline with other contents + 428 # == data 0x2 (new segment) + 429 # 4 5/imm32 + 430 # . write(_test-input-stream, "# comment 1\n") + 431 # . . push args + 432 68/push "# comment 1\n"/imm32 + 433 68/push _test-input-stream/imm32 + 434 # . . call + 435 e8/call write/disp32 + 436 # . . discard args + 437 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 438 # . write(_test-input-stream, " # comment 2 indented\n") + 439 # . . push args + 440 68/push " # comment 2 indented\n"/imm32 + 441 68/push _test-input-stream/imm32 + 442 # . . call + 443 e8/call write/disp32 + 444 # . . discard args + 445 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 446 # . write(_test-input-stream, "== code 0x1\n") + 447 # . . push args + 448 68/push "== code 0x1\n"/imm32 + 449 68/push _test-input-stream/imm32 + 450 # . . call + 451 e8/call write/disp32 + 452 # . . discard args + 453 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 454 # . write(_test-input-stream, "# comment 3 inside a segment\n") + 455 # . . push args + 456 68/push "# comment 3 inside a segment\n"/imm32 + 457 68/push _test-input-stream/imm32 + 458 # . . call + 459 e8/call write/disp32 + 460 # . . discard args + 461 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 462 # . write(_test-input-stream, "1\n") + 463 # . . push args + 464 68/push "1\n"/imm32 + 465 68/push _test-input-stream/imm32 + 466 # . . call + 467 e8/call write/disp32 + 468 # . . discard args + 469 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 470 # . write(_test-input-stream, "\n") # empty line + 471 # . . push args + 472 68/push "\n"/imm32 + 473 68/push _test-input-stream/imm32 + 474 # . . call + 475 e8/call write/disp32 + 476 # . . discard args + 477 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 478 # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n") + 479 # . . push args + 480 68/push "2 3 # comment 4 inline with other contents\n"/imm32 + 481 68/push _test-input-stream/imm32 + 482 # . . call + 483 e8/call write/disp32 + 484 # . . discard args + 485 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 486 # . write(_test-input-stream, "== data 0x2\n") + 487 # . . push args + 488 68/push "== data 0x2\n"/imm32 + 489 68/push _test-input-stream/imm32 + 490 # . . call + 491 e8/call write/disp32 + 492 # . . discard args + 493 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 494 # . write(_test-input-stream, "4 5/imm32\n") + 495 # . . push args + 496 68/push "4 5/imm32\n"/imm32 + 497 68/push _test-input-stream/imm32 + 498 # . . call + 499 e8/call write/disp32 + 500 # . . discard args + 501 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 502 # convert(_test-input-buffered-file, _test-output-buffered-file) + 503 # . . push args + 504 68/push _test-output-buffered-file/imm32 + 505 68/push _test-input-buffered-file/imm32 + 506 # . . call + 507 e8/call convert/disp32 + 508 # . . discard args + 509 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 510 # . flush(_test-output-buffered-file) + 511 # . . push args + 512 68/push _test-output-buffered-file/imm32 + 513 # . . call + 514 e8/call flush/disp32 + 515 # . . discard args + 516 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 517 # check output + 518 # (comment dropped for now) + 519 # (comment dropped for now) + 520 # == code 0x1 + 521 # (comment dropped for now) + 522 # 1 523 # (comment dropped for now) - 524 # (comment dropped for now) - 525 # == code 0x1 - 526 # (comment dropped for now) - 527 # 1 - 528 # (comment dropped for now) - 529 # 2 3 - 530 # == data 0x2 - 531 # 4 5/imm32 - 532 # We don't care right now what exactly happens to comments. Trailing spaces are also minor details. - 533 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- - 559 # . check-next-stream-line-equal(_test-output-stream, "", msg) - 560 # . . push args - 561 68/push "F - test-convert-is-idempotent-by-default/0"/imm32 - 562 68/push ""/imm32 - 563 68/push _test-output-stream/imm32 - 564 # . . call - 565 e8/call check-next-stream-line-equal/disp32 - 566 # . . discard args - 567 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 568 # . check-next-stream-line-equal(_test-output-stream, "", msg) - 569 # . . push args - 570 68/push "F - test-convert-is-idempotent-by-default/1"/imm32 - 571 68/push ""/imm32 - 572 68/push _test-output-stream/imm32 - 573 # . . call - 574 e8/call check-next-stream-line-equal/disp32 - 575 # . . discard args - 576 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 577 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) - 578 # . . push args - 579 68/push "F - test-convert-is-idempotent-by-default/2"/imm32 - 580 68/push "== code 0x1 "/imm32 - 581 68/push _test-output-stream/imm32 - 582 # . . call - 583 e8/call check-next-stream-line-equal/disp32 - 584 # . . discard args - 585 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 586 # . check-next-stream-line-equal(_test-output-stream, "", msg) - 587 # . . push args - 588 68/push "F - test-convert-is-idempotent-by-default/3"/imm32 - 589 68/push ""/imm32 - 590 68/push _test-output-stream/imm32 - 591 # . . call - 592 e8/call check-next-stream-line-equal/disp32 - 593 # . . discard args - 594 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 595 # . check-next-stream-line-equal(_test-output-stream, "1 ", msg) - 596 # . . push args - 597 68/push "F - test-convert-is-idempotent-by-default/4"/imm32 - 598 68/push "1 "/imm32 - 599 68/push _test-output-stream/imm32 - 600 # . . call - 601 e8/call check-next-stream-line-equal/disp32 - 602 # . . discard args - 603 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 604 # . check-next-stream-line-equal(_test-output-stream, "", msg) - 605 # . . push args - 606 68/push "F - test-convert-is-idempotent-by-default/5"/imm32 - 607 68/push ""/imm32 - 608 68/push _test-output-stream/imm32 - 609 # . . call - 610 e8/call check-next-stream-line-equal/disp32 - 611 # . . discard args - 612 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 613 # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg) - 614 # . . push args - 615 68/push "F - test-convert-is-idempotent-by-default/6"/imm32 - 616 68/push "2 3 "/imm32 - 617 68/push _test-output-stream/imm32 - 618 # . . call - 619 e8/call check-next-stream-line-equal/disp32 - 620 # . . discard args - 621 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 622 # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg) - 623 # . . push args - 624 68/push "F - test-convert-is-idempotent-by-default/7"/imm32 - 625 68/push "== data 0x2 "/imm32 - 626 68/push _test-output-stream/imm32 - 627 # . . call - 628 e8/call check-next-stream-line-equal/disp32 - 629 # . . discard args - 630 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 631 # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg) - 632 # . . push args - 633 68/push "F - test-convert-is-idempotent-by-default/8"/imm32 - 634 68/push "4 5/imm32 "/imm32 - 635 68/push _test-output-stream/imm32 - 636 # . . call - 637 e8/call check-next-stream-line-equal/disp32 - 638 # . . discard args - 639 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 640 # . epilog - 641 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 642 5d/pop-to-EBP - 643 c3/return - 644 - 645 test-convert-processes-string-literals: - 646 # . prolog - 647 55/push-EBP - 648 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 649 # setup - 650 # . clear-stream(_test-input-stream) - 651 # . . push args - 652 68/push _test-input-stream/imm32 - 653 # . . call - 654 e8/call clear-stream/disp32 - 655 # . . discard args - 656 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 657 # . clear-stream(_test-input-buffered-file+4) - 658 # . . push args - 659 b8/copy-to-EAX _test-input-buffered-file/imm32 - 660 05/add-to-EAX 4/imm32 - 661 50/push-EAX - 662 # . . call - 663 e8/call clear-stream/disp32 - 664 # . . discard args - 665 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 666 # . clear-stream(_test-output-stream) - 667 # . . push args - 668 68/push _test-output-stream/imm32 - 669 # . . call - 670 e8/call clear-stream/disp32 - 671 # . . discard args - 672 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 673 # . clear-stream(_test-output-buffered-file+4) - 674 # . . push args - 675 b8/copy-to-EAX _test-output-buffered-file/imm32 - 676 05/add-to-EAX 4/imm32 - 677 50/push-EAX - 678 # . . call - 679 e8/call clear-stream/disp32 - 680 # . . discard args - 681 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 682 # initialize input (meta comments in parens) - 683 # == code (new segment) - 684 # 1 "a"/x - 685 # 2 "bc"/y - 686 68/push "== code 0x1\n"/imm32 - 687 68/push _test-input-stream/imm32 - 688 # . . call - 689 e8/call write/disp32 - 690 # . . discard args - 691 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 692 # . write(_test-input-stream, "1 \"a\"/x\n") - 693 # . . push args - 694 68/push "1 \"a\"/x\n"/imm32 - 695 68/push _test-input-stream/imm32 - 696 # . . call - 697 e8/call write/disp32 - 698 # . . discard args - 699 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 700 # . write(_test-input-stream, "2 \"bc\"/y\n") - 701 # . . push args - 702 68/push "2 \"bc\"/y\n"/imm32 - 703 68/push _test-input-stream/imm32 - 704 # . . call - 705 e8/call write/disp32 - 706 # . . discard args - 707 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 708 # convert(_test-input-buffered-file, _test-output-buffered-file) - 709 # . . push args - 710 68/push _test-output-buffered-file/imm32 - 711 68/push _test-input-buffered-file/imm32 - 712 # . . call - 713 e8/call convert/disp32 - 714 # . . discard args - 715 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 716 # . flush(_test-output-buffered-file) - 717 # . . push args - 718 68/push _test-output-buffered-file/imm32 - 719 # . . call - 720 e8/call flush/disp32 - 721 # . . discard args - 722 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 723 # check output - 724 # == code 0x1 - 725 # 1 _string1/x - 726 # 2 _string2/y - 727 # == data - 728 # _string1: - 729 # 1/imm32 61/a - 730 # _string2: - 731 # 2/imm32 62/b 63/c - 732 # We don't care right now what exactly happens to comments. Trailing spaces are also minor details. - 733 # - 734 # Open question: how to make this check more robust. - 735 # We don't actually care what the auto-generated string variables are - 736 # called. We just want to make sure instructions using string literals - 737 # switch to a string variable with the right value. - 738 # (Modifying string literals completely off the radar for now.) - 739 +-- 33 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- - 772 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) - 773 # . . push args - 774 68/push "F - test-convert-processes-string-literals/0"/imm32 - 775 68/push "== code 0x1 "/imm32 - 776 68/push _test-output-stream/imm32 - 777 # . . call - 778 e8/call check-next-stream-line-equal/disp32 - 779 # . . discard args - 780 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 781 # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg) - 782 # . . push args - 783 68/push "F - test-convert-processes-string-literals/1"/imm32 - 784 68/push "1 _string1/x "/imm32 - 785 68/push _test-output-stream/imm32 - 786 # . . call - 787 e8/call check-next-stream-line-equal/disp32 - 788 # . . discard args - 789 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 790 # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg) - 791 # . . push args - 792 68/push "F - test-convert-processes-string-literals/2"/imm32 - 793 68/push "2 _string2/y "/imm32 - 794 68/push _test-output-stream/imm32 - 795 # . . call - 796 e8/call check-next-stream-line-equal/disp32 - 797 # . . discard args - 798 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 799 # . check-next-stream-line-equal(_test-output-stream, "== data", msg) - 800 # . . push args - 801 68/push "F - test-convert-processes-string-literals/3"/imm32 - 802 68/push "== data"/imm32 - 803 68/push _test-output-stream/imm32 - 804 # . . call - 805 e8/call check-next-stream-line-equal/disp32 - 806 # . . discard args - 807 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 808 # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg) - 809 # . . push args - 810 68/push "F - test-convert-processes-string-literals/4"/imm32 - 811 68/push "_string1:"/imm32 - 812 68/push _test-output-stream/imm32 - 813 # . . call - 814 e8/call check-next-stream-line-equal/disp32 - 815 # . . discard args - 816 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 817 # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg) - 818 # . . push args - 819 68/push "F - test-convert-processes-string-literals/5"/imm32 - 820 68/push "0x00000001/imm32 61/a "/imm32 - 821 68/push _test-output-stream/imm32 - 822 # . . call - 823 e8/call check-next-stream-line-equal/disp32 - 824 # . . discard args - 825 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 826 # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg) - 827 # . . push args - 828 68/push "F - test-convert-processes-string-literals/6"/imm32 - 829 68/push "_string2:"/imm32 - 830 68/push _test-output-stream/imm32 - 831 # . . call - 832 e8/call check-next-stream-line-equal/disp32 - 833 # . . discard args - 834 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 835 # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg) - 836 # . . push args - 837 68/push "F - test-convert-processes-string-literals/7"/imm32 - 838 68/push "0x00000002/imm32 62/b 63/c "/imm32 - 839 68/push _test-output-stream/imm32 - 840 # . . call - 841 e8/call check-next-stream-line-equal/disp32 - 842 # . . discard args - 843 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 844 # . epilog - 845 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 846 5d/pop-to-EBP - 847 c3/return - 848 - 849 # generate the data segment contents byte by byte for a given slice - 850 emit-string-literal-data: # out : (address stream), word : (address slice) - 851 # pseudocode - 852 # len = string-length-at-start-of-slice(word->start, word->end) - 853 # print(out, "#{len}/imm32 ") - 854 # curr = word->start - 855 # ++curr # skip '"' - 856 # while true - 857 # if (curr >= word->end) break - 858 # c = *curr - 859 # if (c == '"') break - 860 # if (c == '\') ++curr, c = *curr - 861 # append-byte-hex(out, c) - 862 # if c is alphanumeric: - 863 # write(out, "/") - 864 # append-byte(out, c) - 865 # write(out, " ") - 866 # ++curr - 867 # - 868 # . prolog - 869 55/push-EBP - 870 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 871 # . save registers - 872 50/push-EAX - 873 51/push-ECX - 874 52/push-EDX - 875 56/push-ESI - 876 # ESI = word - 877 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 878 # curr/EDX = word->start - 879 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 880 # max/ESI = word->end - 881 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI - 882 $emit-string-literal-data:emit-length: - 883 # len/EAX = string-length-at-start-of-slice(word->start, word->end) - 884 # . . push args - 885 56/push-ESI - 886 52/push-EDX - 887 # . . call - 888 e8/call string-length-at-start-of-slice/disp32 - 889 # . . discard args - 890 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 891 # print(out, "#{len}/imm32 ") - 892 # . print-int32(out, len) - 893 # . . push args - 894 50/push-EAX - 895 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 896 # . . call - 897 e8/call print-int32/disp32 - 898 # . . discard args - 899 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 900 # . write(out, "/imm32 ") - 901 # . . push args - 902 68/push "/imm32 "/imm32 - 903 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 904 # . . call - 905 e8/call write/disp32 - 906 # . . discard args - 907 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 908 $emit-string-literal-data:loop-init: - 909 # ++curr # skip initial '"' - 910 42/increment-EDX - 911 # c/ECX = 0 - 912 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - 913 $emit-string-literal-data:loop: - 914 # if (curr >= max) break - 915 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI - 916 7d/jump-if-greater-or-equal $emit-string-literal-data:end/disp8 - 917 # CL = *curr - 918 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL - 919 # if (ECX == '"') break - 920 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x22/imm32/dquote # compare ECX - 921 74/jump-if-equal $emit-string-literal-data:end/disp8 - 922 # if (ECX == '\') ++curr, ECX = *curr - 923 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x5c/imm32/backslash # compare ECX - 924 75/jump-if-not-equal $emit-string-literal-data:emit/disp8 - 925 # . ++curr - 926 42/increment-EDX - 927 # . if (curr >= max) break - 928 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI - 929 7d/jump-if-greater-or-equal $emit-string-literal-data:end/disp8 - 930 # . CL = *curr - 931 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL - 932 $emit-string-literal-data:emit: - 933 # append-byte-hex(out, CL) - 934 # . . push args - 935 51/push-ECX - 936 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 937 # . . call - 938 e8/call append-byte-hex/disp32 - 939 # . . discard args - 940 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 941 # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}") - 942 # . EAX = is-alphanumeric?(CL) - 943 # . . push args - 944 51/push-ECX - 945 # . . call - 946 e8/call is-alphanumeric?/disp32 - 947 # . . discard args - 948 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 949 # . if (EAX == 0) goto char-done - 950 3d/compare-EAX-and 0/imm32 - 951 74/jump-if-equal $emit-string-literal-data:char-done/disp8 - 952 # . write(out, "/") - 953 # . . push args - 954 68/push Slash/imm32 - 955 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 956 # . . call - 957 e8/call write/disp32 - 958 # . . discard args - 959 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 960 # . append-byte(out, *curr) - 961 # . . push args - 962 51/push-ECX - 963 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 964 # . . call - 965 e8/call append-byte/disp32 - 966 # . . discard args - 967 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 968 $emit-string-literal-data:char-done: - 969 # write(out, " ") - 970 # . . push args - 971 68/push Space/imm32 - 972 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 973 # . . call - 974 e8/call write/disp32 - 975 # . . discard args - 976 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 977 # ++curr - 978 42/increment-EDX - 979 eb/jump $emit-string-literal-data:loop/disp8 - 980 $emit-string-literal-data:end: - 981 # . restore registers - 982 5e/pop-to-ESI - 983 5a/pop-to-EDX - 984 59/pop-to-ECX - 985 58/pop-to-EAX - 986 # . epilog - 987 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 988 5d/pop-to-EBP - 989 c3/return - 990 - 991 is-alphanumeric?: # c : int -> EAX : boolean - 992 # . prolog - 993 55/push-EBP - 994 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 995 # EAX = c - 996 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - 997 # if (EAX < '0') return false - 998 3d/compare-EAX-with 0x30/imm32/0 - 999 7c/jump-if-lesser $is-alphanumeric?:false/disp8 -1000 # if (EAX <= '9') return true -1001 3d/compare-EAX-with 0x39/imm32/9 -1002 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 -1003 # if (EAX < 'A') return false -1004 3d/compare-EAX-with 0x41/imm32/A -1005 7c/jump-if-lesser $is-alphanumeric?:false/disp8 -1006 # if (EAX <= 'Z') return true -1007 3d/compare-EAX-with 0x5a/imm32/Z -1008 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 -1009 # if (EAX < 'a') return false -1010 3d/compare-EAX-with 0x61/imm32/a -1011 7c/jump-if-lesser $is-alphanumeric?:false/disp8 -1012 # if (EAX <= 'z') return true -1013 3d/compare-EAX-with 0x7a/imm32/z -1014 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 -1015 # return false -1016 $is-alphanumeric?:false: -1017 b8/copy-to-EAX 0/imm32/false -1018 eb/jump $is-alphanumeric?:end/disp8 -1019 $is-alphanumeric?:true: -1020 b8/copy-to-EAX 1/imm32/true -1021 $is-alphanumeric?:end: -1022 # . epilog -1023 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1024 5d/pop-to-EBP -1025 c3/return -1026 -1027 test-emit-string-literal-data: -1028 # . prolog -1029 55/push-EBP -1030 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1031 # setup -1032 # . clear-stream(_test-output-stream) -1033 # . . push args -1034 68/push _test-output-stream/imm32 -1035 # . . call -1036 e8/call clear-stream/disp32 -1037 # . . discard args -1038 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1039 # var slice/ECX = '"abc"/d' -1040 68/push _test-slice-abc-metadata-end/imm32 -1041 68/push _test-slice-abc/imm32 -1042 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1043 # emit-string-literal-data(_test-output-stream, slice) -1044 # . . push args -1045 51/push-ECX -1046 68/push _test-output-stream/imm32 -1047 # . . call -1048 e8/call emit-string-literal-data/disp32 -1049 # . . discard args -1050 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1051 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1077 # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg) -1078 # . . push args -1079 68/push "F - test-emit-string-literal-data"/imm32 -1080 68/push "0x00000003/imm32 61/a 62/b 63/c "/imm32 -1081 68/push _test-output-stream/imm32 -1082 # . . call -1083 e8/call check-stream-equal/disp32 -1084 # . . discard args -1085 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1086 # . epilog -1087 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1088 5d/pop-to-EBP -1089 c3/return -1090 -1091 test-emit-string-literal-data-empty: -1092 # . prolog -1093 55/push-EBP -1094 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1095 # setup -1096 # . clear-stream(_test-output-stream) -1097 # . . push args -1098 68/push _test-output-stream/imm32 -1099 # . . call -1100 e8/call clear-stream/disp32 -1101 # . . discard args -1102 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1103 # var slice/ECX = '""' -1104 68/push _test-slice-empty-string-literal-end/imm32 -1105 68/push _test-slice-empty-string-literal/imm32 -1106 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1107 # emit-string-literal-data(_test-output-stream, slice) -1108 # . . push args -1109 51/push-ECX -1110 68/push _test-output-stream/imm32 -1111 # . . call -1112 e8/call emit-string-literal-data/disp32 -1113 # . . discard args -1114 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1115 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1141 # . check-stream-equal(_test-output-stream, "0/imm32 ", msg) -1142 # . . push args -1143 68/push "F - test-emit-string-literal-data-empty"/imm32 -1144 68/push "0x00000000/imm32 "/imm32 -1145 68/push _test-output-stream/imm32 -1146 # . . call -1147 e8/call check-stream-equal/disp32 -1148 # . . discard args -1149 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1150 # . epilog -1151 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1152 5d/pop-to-EBP -1153 c3/return -1154 -1155 # just to keep things simple -1156 test-emit-string-literal-data-no-metadata-for-non-alphanumerics: -1157 # . prolog -1158 55/push-EBP -1159 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1160 # setup -1161 # . clear-stream(_test-output-stream) -1162 # . . push args -1163 68/push _test-output-stream/imm32 -1164 # . . call -1165 e8/call clear-stream/disp32 -1166 # . . discard args -1167 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1168 # var slice/ECX = '"a b"' -1169 68/push _test-slice-a-space-b-end/imm32 -1170 68/push _test-slice-a-space-b/imm32 -1171 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1172 # emit-string-literal-data(_test-output-stream, slice) -1173 # . . push args -1174 51/push-ECX -1175 68/push _test-output-stream/imm32 -1176 # . . call -1177 e8/call emit-string-literal-data/disp32 -1178 # . . discard args -1179 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1180 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1206 # . check-stream-equal(_test-output-stream, "3/imm32 61/a 20 62/b ", msg) # ideally we'd like to say '20/space' but that requires managing names for codepoints -1207 # . . push args -1208 68/push "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32 -1209 68/push "0x00000003/imm32 61/a 20 62/b "/imm32 -1210 68/push _test-output-stream/imm32 -1211 # . . call -1212 e8/call check-stream-equal/disp32 -1213 # . . discard args -1214 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1215 # . epilog -1216 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1217 5d/pop-to-EBP -1218 c3/return -1219 -1220 test-emit-string-literal-data-handles-escape-sequences: -1221 # . prolog -1222 55/push-EBP -1223 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1224 # setup -1225 # . clear-stream(_test-output-stream) -1226 # . . push args -1227 68/push _test-output-stream/imm32 -1228 # . . call -1229 e8/call clear-stream/disp32 -1230 # . . discard args -1231 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1232 # var slice/ECX = '"a\"b"' -1233 68/push _test-slice-a-dquote-b-end/imm32 -1234 68/push _test-slice-a-dquote-b/imm32 -1235 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1236 # emit-string-literal-data(_test-output-stream, slice) -1237 # . . push args -1238 51/push-ECX -1239 68/push _test-output-stream/imm32 -1240 # . . call -1241 e8/call emit-string-literal-data/disp32 -1242 # . . discard args -1243 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1244 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1270 # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg) -1271 # . . push args -1272 68/push "F - test-emit-string-literal-data-handles-escape-sequences"/imm32 -1273 68/push "0x00000003/imm32 61/a 22 62/b "/imm32 -1274 68/push _test-output-stream/imm32 -1275 # . . call -1276 e8/call check-stream-equal/disp32 -1277 # . . discard args -1278 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1279 # . epilog -1280 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1281 5d/pop-to-EBP -1282 c3/return -1283 -1284 # emit everything from a word except the initial datum -1285 emit-metadata: # out : (address buffered-file), word : (address slice) -1286 # pseudocode -1287 # var slice = {0, word->end} -1288 # curr = word->start -1289 # if *curr == '"' -1290 # curr = skip-string-in-slice(curr, word->end) -1291 # else -1292 # while true -1293 # if curr == word->end -1294 # return -1295 # if *curr == '/' -1296 # break -1297 # ++curr -1298 # slice->curr = curr -1299 # write-slice-buffered(out, slice) -1300 # -1301 # . prolog -1302 55/push-EBP -1303 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1304 # . save registers -1305 50/push-EAX -1306 51/push-ECX -1307 52/push-EDX -1308 53/push-EBX -1309 56/push-ESI -1310 # ESI = word -1311 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI -1312 # curr/ECX = word->start -1313 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX -1314 # end/EDX = word->end -1315 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX -1316 # var slice/EBX = {0, end} -1317 52/push-EDX -1318 68/push 0/imm32 -1319 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX -1320 # EAX = 0 -1321 b8/copy-to-EAX 0/imm32 -1322 $emit-metadata:check-for-string-literal: -1323 # - if (*curr == '"') curr = skip-string-in-slice(curr, end) -1324 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -1325 3d/compare-EAX-and 0x22/imm32/dquote -1326 75/jump-if-not-equal $emit-metadata:skip-datum-loop/disp8 -1327 $emit-metadata:skip-string-literal: -1328 # . EAX = skip-string-in-slice(curr, end) -1329 # . . push args -1330 52/push-EDX -1331 51/push-ECX -1332 # . . call -1333 e8/call skip-string-in-slice/disp32 -1334 # . . discard args -1335 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1336 # . curr = EAX -1337 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX -1338 eb/jump $emit-metadata:emit/disp8 -1339 $emit-metadata:skip-datum-loop: -1340 # - otherwise scan for '/' -1341 # if (curr == end) return -1342 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX and EDX -1343 74/jump-if-equal $emit-metadata:end/disp8 -1344 # if (*curr == '/') break -1345 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -1346 3d/compare-EAX-and 0x2f/imm32/slash -1347 74/jump-if-equal $emit-metadata:emit/disp8 -1348 # ++curr -1349 41/increment-ECX -1350 eb/jump $emit-metadata:skip-datum-loop/disp8 -1351 $emit-metadata:emit: -1352 # slice->curr = ECX -1353 89/copy 0/mod/indirect 3/rm32/EBX . . . 1/r32/ECX . . # copy ECX to *EBX -1354 # write-slice-buffered(out, slice) -1355 # . . push args -1356 53/push-EBX -1357 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1358 # . . call -1359 e8/call write-slice-buffered/disp32 -1360 # . . discard args -1361 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP -1362 $emit-metadata:end: -1363 # . reclaim locals -1364 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP -1365 # . restore registers -1366 5e/pop-to-ESI -1367 5b/pop-to-EBX -1368 5a/pop-to-EDX -1369 59/pop-to-ECX -1370 58/pop-to-EAX -1371 # . epilog -1372 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1373 5d/pop-to-EBP -1374 c3/return -1375 -1376 test-emit-metadata: -1377 # . prolog -1378 55/push-EBP -1379 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1380 # setup -1381 # . clear-stream(_test-output-stream) -1382 # . . push args -1383 68/push _test-output-stream/imm32 -1384 # . . call -1385 e8/call clear-stream/disp32 -1386 # . . discard args -1387 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1388 # . clear-stream(_test-output-buffered-file+4) -1389 # . . push args -1390 b8/copy-to-EAX _test-output-buffered-file/imm32 -1391 05/add-to-EAX 4/imm32 -1392 50/push-EAX -1393 # . . call -1394 e8/call clear-stream/disp32 -1395 # . . discard args -1396 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1397 # var slice/ECX = "abc/def" -1398 68/push _test-slice-word-end/imm32 -1399 68/push _test-slice-word/imm32/start + 524 # 2 3 + 525 # == data 0x2 + 526 # 4 5/imm32 + 527 # We don't care right now what exactly happens to comments. Trailing spaces are also minor details. + 528 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- + 554 # . check-next-stream-line-equal(_test-output-stream, "", msg) + 555 # . . push args + 556 68/push "F - test-convert-is-idempotent-by-default/0"/imm32 + 557 68/push ""/imm32 + 558 68/push _test-output-stream/imm32 + 559 # . . call + 560 e8/call check-next-stream-line-equal/disp32 + 561 # . . discard args + 562 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 563 # . check-next-stream-line-equal(_test-output-stream, "", msg) + 564 # . . push args + 565 68/push "F - test-convert-is-idempotent-by-default/1"/imm32 + 566 68/push ""/imm32 + 567 68/push _test-output-stream/imm32 + 568 # . . call + 569 e8/call check-next-stream-line-equal/disp32 + 570 # . . discard args + 571 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 572 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) + 573 # . . push args + 574 68/push "F - test-convert-is-idempotent-by-default/2"/imm32 + 575 68/push "== code 0x1 "/imm32 + 576 68/push _test-output-stream/imm32 + 577 # . . call + 578 e8/call check-next-stream-line-equal/disp32 + 579 # . . discard args + 580 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 581 # . check-next-stream-line-equal(_test-output-stream, "", msg) + 582 # . . push args + 583 68/push "F - test-convert-is-idempotent-by-default/3"/imm32 + 584 68/push ""/imm32 + 585 68/push _test-output-stream/imm32 + 586 # . . call + 587 e8/call check-next-stream-line-equal/disp32 + 588 # . . discard args + 589 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 590 # . check-next-stream-line-equal(_test-output-stream, "1 ", msg) + 591 # . . push args + 592 68/push "F - test-convert-is-idempotent-by-default/4"/imm32 + 593 68/push "1 "/imm32 + 594 68/push _test-output-stream/imm32 + 595 # . . call + 596 e8/call check-next-stream-line-equal/disp32 + 597 # . . discard args + 598 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 599 # . check-next-stream-line-equal(_test-output-stream, "", msg) + 600 # . . push args + 601 68/push "F - test-convert-is-idempotent-by-default/5"/imm32 + 602 68/push ""/imm32 + 603 68/push _test-output-stream/imm32 + 604 # . . call + 605 e8/call check-next-stream-line-equal/disp32 + 606 # . . discard args + 607 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 608 # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg) + 609 # . . push args + 610 68/push "F - test-convert-is-idempotent-by-default/6"/imm32 + 611 68/push "2 3 "/imm32 + 612 68/push _test-output-stream/imm32 + 613 # . . call + 614 e8/call check-next-stream-line-equal/disp32 + 615 # . . discard args + 616 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 617 # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg) + 618 # . . push args + 619 68/push "F - test-convert-is-idempotent-by-default/7"/imm32 + 620 68/push "== data 0x2 "/imm32 + 621 68/push _test-output-stream/imm32 + 622 # . . call + 623 e8/call check-next-stream-line-equal/disp32 + 624 # . . discard args + 625 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 626 # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg) + 627 # . . push args + 628 68/push "F - test-convert-is-idempotent-by-default/8"/imm32 + 629 68/push "4 5/imm32 "/imm32 + 630 68/push _test-output-stream/imm32 + 631 # . . call + 632 e8/call check-next-stream-line-equal/disp32 + 633 # . . discard args + 634 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 635 # . epilog + 636 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 637 5d/pop-to-EBP + 638 c3/return + 639 + 640 test-convert-processes-string-literals: + 641 # . prolog + 642 55/push-EBP + 643 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 644 # setup + 645 # . clear-stream(_test-input-stream) + 646 # . . push args + 647 68/push _test-input-stream/imm32 + 648 # . . call + 649 e8/call clear-stream/disp32 + 650 # . . discard args + 651 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 652 # . clear-stream(_test-input-buffered-file+4) + 653 # . . push args + 654 b8/copy-to-EAX _test-input-buffered-file/imm32 + 655 05/add-to-EAX 4/imm32 + 656 50/push-EAX + 657 # . . call + 658 e8/call clear-stream/disp32 + 659 # . . discard args + 660 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 661 # . clear-stream(_test-output-stream) + 662 # . . push args + 663 68/push _test-output-stream/imm32 + 664 # . . call + 665 e8/call clear-stream/disp32 + 666 # . . discard args + 667 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 668 # . clear-stream(_test-output-buffered-file+4) + 669 # . . push args + 670 b8/copy-to-EAX _test-output-buffered-file/imm32 + 671 05/add-to-EAX 4/imm32 + 672 50/push-EAX + 673 # . . call + 674 e8/call clear-stream/disp32 + 675 # . . discard args + 676 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 677 # initialize input (meta comments in parens) + 678 # == code (new segment) + 679 # 1 "a"/x + 680 # 2 "bc"/y + 681 68/push "== code 0x1\n"/imm32 + 682 68/push _test-input-stream/imm32 + 683 # . . call + 684 e8/call write/disp32 + 685 # . . discard args + 686 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 687 # . write(_test-input-stream, "1 \"a\"/x\n") + 688 # . . push args + 689 68/push "1 \"a\"/x\n"/imm32 + 690 68/push _test-input-stream/imm32 + 691 # . . call + 692 e8/call write/disp32 + 693 # . . discard args + 694 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 695 # . write(_test-input-stream, "2 \"bc\"/y\n") + 696 # . . push args + 697 68/push "2 \"bc\"/y\n"/imm32 + 698 68/push _test-input-stream/imm32 + 699 # . . call + 700 e8/call write/disp32 + 701 # . . discard args + 702 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 703 # convert(_test-input-buffered-file, _test-output-buffered-file) + 704 # . . push args + 705 68/push _test-output-buffered-file/imm32 + 706 68/push _test-input-buffered-file/imm32 + 707 # . . call + 708 e8/call convert/disp32 + 709 # . . discard args + 710 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 711 # . flush(_test-output-buffered-file) + 712 # . . push args + 713 68/push _test-output-buffered-file/imm32 + 714 # . . call + 715 e8/call flush/disp32 + 716 # . . discard args + 717 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 718 # check output + 719 # == code 0x1 + 720 # 1 _string1/x + 721 # 2 _string2/y + 722 # == data + 723 # _string1: + 724 # 1/imm32 61/a + 725 # _string2: + 726 # 2/imm32 62/b 63/c + 727 # We don't care right now what exactly happens to comments. Trailing spaces are also minor details. + 728 # + 729 # Open question: how to make this check more robust. + 730 # We don't actually care what the auto-generated string variables are + 731 # called. We just want to make sure instructions using string literals + 732 # switch to a string variable with the right value. + 733 # (Modifying string literals completely off the radar for now.) + 734 +-- 33 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- + 767 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) + 768 # . . push args + 769 68/push "F - test-convert-processes-string-literals/0"/imm32 + 770 68/push "== code 0x1 "/imm32 + 771 68/push _test-output-stream/imm32 + 772 # . . call + 773 e8/call check-next-stream-line-equal/disp32 + 774 # . . discard args + 775 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 776 # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg) + 777 # . . push args + 778 68/push "F - test-convert-processes-string-literals/1"/imm32 + 779 68/push "1 _string1/x "/imm32 + 780 68/push _test-output-stream/imm32 + 781 # . . call + 782 e8/call check-next-stream-line-equal/disp32 + 783 # . . discard args + 784 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 785 # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg) + 786 # . . push args + 787 68/push "F - test-convert-processes-string-literals/2"/imm32 + 788 68/push "2 _string2/y "/imm32 + 789 68/push _test-output-stream/imm32 + 790 # . . call + 791 e8/call check-next-stream-line-equal/disp32 + 792 # . . discard args + 793 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 794 # . check-next-stream-line-equal(_test-output-stream, "== data", msg) + 795 # . . push args + 796 68/push "F - test-convert-processes-string-literals/3"/imm32 + 797 68/push "== data"/imm32 + 798 68/push _test-output-stream/imm32 + 799 # . . call + 800 e8/call check-next-stream-line-equal/disp32 + 801 # . . discard args + 802 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 803 # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg) + 804 # . . push args + 805 68/push "F - test-convert-processes-string-literals/4"/imm32 + 806 68/push "_string1:"/imm32 + 807 68/push _test-output-stream/imm32 + 808 # . . call + 809 e8/call check-next-stream-line-equal/disp32 + 810 # . . discard args + 811 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 812 # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg) + 813 # . . push args + 814 68/push "F - test-convert-processes-string-literals/5"/imm32 + 815 68/push "0x00000001/imm32 61/a "/imm32 + 816 68/push _test-output-stream/imm32 + 817 # . . call + 818 e8/call check-next-stream-line-equal/disp32 + 819 # . . discard args + 820 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 821 # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg) + 822 # . . push args + 823 68/push "F - test-convert-processes-string-literals/6"/imm32 + 824 68/push "_string2:"/imm32 + 825 68/push _test-output-stream/imm32 + 826 # . . call + 827 e8/call check-next-stream-line-equal/disp32 + 828 # . . discard args + 829 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 830 # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg) + 831 # . . push args + 832 68/push "F - test-convert-processes-string-literals/7"/imm32 + 833 68/push "0x00000002/imm32 62/b 63/c "/imm32 + 834 68/push _test-output-stream/imm32 + 835 # . . call + 836 e8/call check-next-stream-line-equal/disp32 + 837 # . . discard args + 838 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 839 # . epilog + 840 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 841 5d/pop-to-EBP + 842 c3/return + 843 + 844 # generate the data segment contents byte by byte for a given slice + 845 emit-string-literal-data: # out : (address stream), word : (address slice) + 846 # pseudocode + 847 # len = string-length-at-start-of-slice(word->start, word->end) + 848 # print(out, "#{len}/imm32 ") + 849 # curr = word->start + 850 # ++curr # skip '"' + 851 # while true + 852 # if (curr >= word->end) break + 853 # c = *curr + 854 # if (c == '"') break + 855 # if (c == '\') ++curr, c = *curr + 856 # append-byte-hex(out, c) + 857 # if c is alphanumeric: + 858 # write(out, "/") + 859 # append-byte(out, c) + 860 # write(out, " ") + 861 # ++curr + 862 # + 863 # . prolog + 864 55/push-EBP + 865 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 866 # . save registers + 867 50/push-EAX + 868 51/push-ECX + 869 52/push-EDX + 870 56/push-ESI + 871 # ESI = word + 872 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + 873 # curr/EDX = word->start + 874 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX + 875 # max/ESI = word->end + 876 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI + 877 $emit-string-literal-data:emit-length: + 878 # len/EAX = string-length-at-start-of-slice(word->start, word->end) + 879 # . . push args + 880 56/push-ESI + 881 52/push-EDX + 882 # . . call + 883 e8/call string-length-at-start-of-slice/disp32 + 884 # . . discard args + 885 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 886 # print(out, "#{len}/imm32 ") + 887 # . print-int32(out, len) + 888 # . . push args + 889 50/push-EAX + 890 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 891 # . . call + 892 e8/call print-int32/disp32 + 893 # . . discard args + 894 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 895 # . write(out, "/imm32 ") + 896 # . . push args + 897 68/push "/imm32 "/imm32 + 898 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 899 # . . call + 900 e8/call write/disp32 + 901 # . . discard args + 902 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 903 $emit-string-literal-data:loop-init: + 904 # ++curr # skip initial '"' + 905 42/increment-EDX + 906 # c/ECX = 0 + 907 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX + 908 $emit-string-literal-data:loop: + 909 # if (curr >= max) break + 910 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI + 911 73/jump-if-greater-or-equal-unsigned $emit-string-literal-data:end/disp8 + 912 # CL = *curr + 913 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL + 914 # if (ECX == '"') break + 915 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x22/imm32/dquote # compare ECX + 916 74/jump-if-equal $emit-string-literal-data:end/disp8 + 917 # if (ECX == '\') ++curr, ECX = *curr + 918 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x5c/imm32/backslash # compare ECX + 919 75/jump-if-not-equal $emit-string-literal-data:emit/disp8 + 920 # . ++curr + 921 42/increment-EDX + 922 # . if (curr >= max) break + 923 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI + 924 73/jump-if-greater-or-equal-unsigned $emit-string-literal-data:end/disp8 + 925 # . CL = *curr + 926 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL + 927 $emit-string-literal-data:emit: + 928 # append-byte-hex(out, CL) + 929 # . . push args + 930 51/push-ECX + 931 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 932 # . . call + 933 e8/call append-byte-hex/disp32 + 934 # . . discard args + 935 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 936 # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}") + 937 # . EAX = is-alphanumeric?(CL) + 938 # . . push args + 939 51/push-ECX + 940 # . . call + 941 e8/call is-alphanumeric?/disp32 + 942 # . . discard args + 943 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 944 # . if (EAX == 0) goto char-done + 945 3d/compare-EAX-and 0/imm32 + 946 74/jump-if-equal $emit-string-literal-data:char-done/disp8 + 947 # . write(out, "/") + 948 # . . push args + 949 68/push Slash/imm32 + 950 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 951 # . . call + 952 e8/call write/disp32 + 953 # . . discard args + 954 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 955 # . append-byte(out, *curr) + 956 # . . push args + 957 51/push-ECX + 958 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 959 # . . call + 960 e8/call append-byte/disp32 + 961 # . . discard args + 962 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 963 $emit-string-literal-data:char-done: + 964 # write(out, " ") + 965 # . . push args + 966 68/push Space/imm32 + 967 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 968 # . . call + 969 e8/call write/disp32 + 970 # . . discard args + 971 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 972 # ++curr + 973 42/increment-EDX + 974 eb/jump $emit-string-literal-data:loop/disp8 + 975 $emit-string-literal-data:end: + 976 # . restore registers + 977 5e/pop-to-ESI + 978 5a/pop-to-EDX + 979 59/pop-to-ECX + 980 58/pop-to-EAX + 981 # . epilog + 982 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 983 5d/pop-to-EBP + 984 c3/return + 985 + 986 is-alphanumeric?: # c : int -> EAX : boolean + 987 # . prolog + 988 55/push-EBP + 989 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 990 # EAX = c + 991 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + 992 # if (EAX < '0') return false + 993 3d/compare-EAX-with 0x30/imm32/0 + 994 7c/jump-if-lesser $is-alphanumeric?:false/disp8 + 995 # if (EAX <= '9') return true + 996 3d/compare-EAX-with 0x39/imm32/9 + 997 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 + 998 # if (EAX < 'A') return false + 999 3d/compare-EAX-with 0x41/imm32/A +1000 7c/jump-if-lesser $is-alphanumeric?:false/disp8 +1001 # if (EAX <= 'Z') return true +1002 3d/compare-EAX-with 0x5a/imm32/Z +1003 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 +1004 # if (EAX < 'a') return false +1005 3d/compare-EAX-with 0x61/imm32/a +1006 7c/jump-if-lesser $is-alphanumeric?:false/disp8 +1007 # if (EAX <= 'z') return true +1008 3d/compare-EAX-with 0x7a/imm32/z +1009 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 +1010 # return false +1011 $is-alphanumeric?:false: +1012 b8/copy-to-EAX 0/imm32/false +1013 eb/jump $is-alphanumeric?:end/disp8 +1014 $is-alphanumeric?:true: +1015 b8/copy-to-EAX 1/imm32/true +1016 $is-alphanumeric?:end: +1017 # . epilog +1018 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1019 5d/pop-to-EBP +1020 c3/return +1021 +1022 test-emit-string-literal-data: +1023 # . prolog +1024 55/push-EBP +1025 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1026 # setup +1027 # . clear-stream(_test-output-stream) +1028 # . . push args +1029 68/push _test-output-stream/imm32 +1030 # . . call +1031 e8/call clear-stream/disp32 +1032 # . . discard args +1033 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1034 # var slice/ECX = '"abc"/d' +1035 68/push _test-slice-abc-limit/imm32 +1036 68/push _test-slice-abc/imm32 +1037 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1038 # emit-string-literal-data(_test-output-stream, slice) +1039 # . . push args +1040 51/push-ECX +1041 68/push _test-output-stream/imm32 +1042 # . . call +1043 e8/call emit-string-literal-data/disp32 +1044 # . . discard args +1045 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1046 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1072 # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg) +1073 # . . push args +1074 68/push "F - test-emit-string-literal-data"/imm32 +1075 68/push "0x00000003/imm32 61/a 62/b 63/c "/imm32 +1076 68/push _test-output-stream/imm32 +1077 # . . call +1078 e8/call check-stream-equal/disp32 +1079 # . . discard args +1080 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1081 # . epilog +1082 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1083 5d/pop-to-EBP +1084 c3/return +1085 +1086 test-emit-string-literal-data-empty: +1087 # . prolog +1088 55/push-EBP +1089 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1090 # setup +1091 # . clear-stream(_test-output-stream) +1092 # . . push args +1093 68/push _test-output-stream/imm32 +1094 # . . call +1095 e8/call clear-stream/disp32 +1096 # . . discard args +1097 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1098 # var slice/ECX = '""' +1099 68/push 0/imm32/end +1100 68/push 0/imm32/start +1101 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1102 # emit-string-literal-data(_test-output-stream, slice) +1103 # . . push args +1104 51/push-ECX +1105 68/push _test-output-stream/imm32 +1106 # . . call +1107 e8/call emit-string-literal-data/disp32 +1108 # . . discard args +1109 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1110 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1136 # . check-stream-equal(_test-output-stream, "0/imm32 ", msg) +1137 # . . push args +1138 68/push "F - test-emit-string-literal-data-empty"/imm32 +1139 68/push "0x00000000/imm32 "/imm32 +1140 68/push _test-output-stream/imm32 +1141 # . . call +1142 e8/call check-stream-equal/disp32 +1143 # . . discard args +1144 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1145 # . epilog +1146 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1147 5d/pop-to-EBP +1148 c3/return +1149 +1150 # just to keep things simple +1151 test-emit-string-literal-data-no-metadata-for-non-alphanumerics: +1152 # . prolog +1153 55/push-EBP +1154 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1155 # setup +1156 # . clear-stream(_test-output-stream) +1157 # . . push args +1158 68/push _test-output-stream/imm32 +1159 # . . call +1160 e8/call clear-stream/disp32 +1161 # . . discard args +1162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1163 # var slice/ECX = '"a b"' +1164 68/push _test-slice-a-space-b-limit/imm32 +1165 68/push _test-slice-a-space-b/imm32 +1166 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1167 # emit-string-literal-data(_test-output-stream, slice) +1168 # . . push args +1169 51/push-ECX +1170 68/push _test-output-stream/imm32 +1171 # . . call +1172 e8/call emit-string-literal-data/disp32 +1173 # . . discard args +1174 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1175 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1201 # . check-stream-equal(_test-output-stream, "3/imm32 61/a 20 62/b ", msg) # ideally we'd like to say '20/space' but that requires managing names for codepoints +1202 # . . push args +1203 68/push "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32 +1204 68/push "0x00000003/imm32 61/a 20 62/b "/imm32 +1205 68/push _test-output-stream/imm32 +1206 # . . call +1207 e8/call check-stream-equal/disp32 +1208 # . . discard args +1209 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1210 # . epilog +1211 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1212 5d/pop-to-EBP +1213 c3/return +1214 +1215 test-emit-string-literal-data-handles-escape-sequences: +1216 # . prolog +1217 55/push-EBP +1218 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1219 # setup +1220 # . clear-stream(_test-output-stream) +1221 # . . push args +1222 68/push _test-output-stream/imm32 +1223 # . . call +1224 e8/call clear-stream/disp32 +1225 # . . discard args +1226 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1227 # var slice/ECX = '"a\"b"' +1228 68/push _test-slice-a-dquote-b-limit/imm32 +1229 68/push _test-slice-a-dquote-b/imm32 +1230 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1231 # emit-string-literal-data(_test-output-stream, slice) +1232 # . . push args +1233 51/push-ECX +1234 68/push _test-output-stream/imm32 +1235 # . . call +1236 e8/call emit-string-literal-data/disp32 +1237 # . . discard args +1238 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1239 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1265 # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg) +1266 # . . push args +1267 68/push "F - test-emit-string-literal-data-handles-escape-sequences"/imm32 +1268 68/push "0x00000003/imm32 61/a 22 62/b "/imm32 +1269 68/push _test-output-stream/imm32 +1270 # . . call +1271 e8/call check-stream-equal/disp32 +1272 # . . discard args +1273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1274 # . epilog +1275 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1276 5d/pop-to-EBP +1277 c3/return +1278 +1279 # emit everything from a word except the initial datum +1280 emit-metadata: # out : (address buffered-file), word : (address slice) +1281 # pseudocode +1282 # var slice = {0, word->end} +1283 # curr = word->start +1284 # if *curr == '"' +1285 # curr = skip-string-in-slice(curr, word->end) +1286 # else +1287 # while true +1288 # if curr == word->end +1289 # return +1290 # if *curr == '/' +1291 # break +1292 # ++curr +1293 # slice->start = curr +1294 # write-slice-buffered(out, slice) +1295 # +1296 # . prolog +1297 55/push-EBP +1298 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1299 # . save registers +1300 50/push-EAX +1301 51/push-ECX +1302 52/push-EDX +1303 53/push-EBX +1304 56/push-ESI +1305 # ESI = word +1306 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI +1307 # curr/ECX = word->start +1308 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX +1309 # end/EDX = word->end +1310 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX +1311 # var slice/EBX = {0, end} +1312 52/push-EDX +1313 68/push 0/imm32 +1314 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX +1315 # EAX = 0 +1316 b8/copy-to-EAX 0/imm32 +1317 $emit-metadata:check-for-string-literal: +1318 # - if (*curr == '"') curr = skip-string-in-slice(curr, end) +1319 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +1320 3d/compare-EAX-and 0x22/imm32/dquote +1321 75/jump-if-not-equal $emit-metadata:skip-datum-loop/disp8 +1322 $emit-metadata:skip-string-literal: +1323 # . EAX = skip-string-in-slice(curr, end) +1324 # . . push args +1325 52/push-EDX +1326 51/push-ECX +1327 # . . call +1328 e8/call skip-string-in-slice/disp32 +1329 # . . discard args +1330 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1331 # . curr = EAX +1332 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX +1333 eb/jump $emit-metadata:emit/disp8 +1334 $emit-metadata:skip-datum-loop: +1335 # - otherwise scan for '/' +1336 # if (curr == end) return +1337 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX and EDX +1338 74/jump-if-equal $emit-metadata:end/disp8 +1339 # if (*curr == '/') break +1340 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +1341 3d/compare-EAX-and 0x2f/imm32/slash +1342 74/jump-if-equal $emit-metadata:emit/disp8 +1343 # ++curr +1344 41/increment-ECX +1345 eb/jump $emit-metadata:skip-datum-loop/disp8 +1346 $emit-metadata:emit: +1347 # slice->start = ECX +1348 89/copy 0/mod/indirect 3/rm32/EBX . . . 1/r32/ECX . . # copy ECX to *EBX +1349 # write-slice-buffered(out, slice) +1350 # . . push args +1351 53/push-EBX +1352 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1353 # . . call +1354 e8/call write-slice-buffered/disp32 +1355 # . . discard args +1356 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP +1357 $emit-metadata:end: +1358 # . reclaim locals +1359 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP +1360 # . restore registers +1361 5e/pop-to-ESI +1362 5b/pop-to-EBX +1363 5a/pop-to-EDX +1364 59/pop-to-ECX +1365 58/pop-to-EAX +1366 # . epilog +1367 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1368 5d/pop-to-EBP +1369 c3/return +1370 +1371 test-emit-metadata: +1372 # . prolog +1373 55/push-EBP +1374 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1375 # setup +1376 # . clear-stream(_test-output-stream) +1377 # . . push args +1378 68/push _test-output-stream/imm32 +1379 # . . call +1380 e8/call clear-stream/disp32 +1381 # . . discard args +1382 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1383 # . clear-stream(_test-output-buffered-file+4) +1384 # . . push args +1385 b8/copy-to-EAX _test-output-buffered-file/imm32 +1386 05/add-to-EAX 4/imm32 +1387 50/push-EAX +1388 # . . call +1389 e8/call clear-stream/disp32 +1390 # . . discard args +1391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1392 # (EAX..ECX) = "abc/def" +1393 b8/copy-to-EAX "abc/def"/imm32 +1394 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1395 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +1396 05/add-to-EAX 4/imm32 +1397 # var slice/ECX = {EAX, ECX} +1398 51/push-ECX +1399 50/push-EAX 1400 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX 1401 # emit-metadata(_test-output-buffered-file, slice) 1402 # . . push args 1403 51/push-ECX 1404 68/push _test-output-buffered-file/imm32 1405 # . . call -1406 e8/call emit-metadata/disp32 +1406 e8/call emit-metadata/disp32 1407 # . . discard args 1408 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 1409 # flush(_test-output-buffered-file) @@ -1326,7 +1325,7 @@ if ('onhashchange' in window) { 1419 68/push "/def"/imm32 1420 68/push _test-output-stream/imm32 1421 # . . call -1422 e8/call check-stream-equal/disp32 +1422 e8/call check-stream-equal/disp32 1423 # . . discard args 1424 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 1425 # . epilog @@ -1355,1225 +1354,1214 @@ if ('onhashchange' in window) { 1448 e8/call clear-stream/disp32 1449 # . . discard args 1450 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1451 # var slice/ECX = "abc" -1452 68/push _test-slice-word-datum-end/imm32 -1453 68/push _test-slice-word/imm32/start -1454 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1455 # emit-metadata(_test-output-buffered-file, slice) -1456 # . . push args +1451 # (EAX..ECX) = "abc" +1452 b8/copy-to-EAX "abc"/imm32 +1453 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1454 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +1455 05/add-to-EAX 4/imm32 +1456 # var slice/ECX = {EAX, ECX} 1457 51/push-ECX -1458 68/push _test-output-buffered-file/imm32 -1459 # . . call -1460 e8/call emit-metadata/disp32 -1461 # . . discard args -1462 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1463 # flush(_test-output-buffered-file) -1464 # . . push args -1465 68/push _test-output-buffered-file/imm32 -1466 # . . call -1467 e8/call flush/disp32 -1468 # . . discard args -1469 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1470 # check-stream-equal(_test-output-stream, "", msg) -1471 # . . push args -1472 68/push "F - test-emit-metadata-none"/imm32 -1473 68/push ""/imm32 -1474 68/push _test-output-stream/imm32 -1475 # . . call -1476 e8/call check-stream-equal/disp32 -1477 # . . discard args -1478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1479 # . epilog -1480 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1481 5d/pop-to-EBP -1482 c3/return -1483 -1484 test-emit-metadata-multiple: -1485 # . prolog -1486 55/push-EBP -1487 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1488 # setup -1489 # . clear-stream(_test-output-stream) -1490 # . . push args -1491 68/push _test-output-stream/imm32 -1492 # . . call -1493 e8/call clear-stream/disp32 -1494 # . . discard args -1495 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1496 # . clear-stream(_test-output-buffered-file+4) -1497 # . . push args -1498 b8/copy-to-EAX _test-output-buffered-file/imm32 -1499 05/add-to-EAX 4/imm32 -1500 50/push-EAX -1501 # . . call -1502 e8/call clear-stream/disp32 -1503 # . . discard args -1504 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1505 # var slice/ECX = "abc/def/ghi" -1506 68/push _test-slice-word-end2/imm32 -1507 68/push _test-slice-word/imm32/start -1508 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1509 # emit-metadata(_test-output-buffered-file, slice) -1510 # . . push args -1511 51/push-ECX -1512 68/push _test-output-buffered-file/imm32 -1513 # . . call -1514 e8/call emit-metadata/disp32 -1515 # . . discard args -1516 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1517 # flush(_test-output-buffered-file) -1518 # . . push args -1519 68/push _test-output-buffered-file/imm32 -1520 # . . call -1521 e8/call flush/disp32 -1522 # . . discard args -1523 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1524 # check-stream-equal(_test-output-stream, "/def/ghi", msg) # important that there's no leading space -1525 # . . push args -1526 68/push "F - test-emit-metadata-multiple"/imm32 -1527 68/push "/def/ghi"/imm32 -1528 68/push _test-output-stream/imm32 -1529 # . . call -1530 e8/call check-stream-equal/disp32 -1531 # . . discard args -1532 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1533 # . epilog -1534 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1535 5d/pop-to-EBP -1536 c3/return -1537 -1538 test-emit-metadata-when-no-datum: -1539 # . prolog -1540 55/push-EBP -1541 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1542 # setup -1543 # . clear-stream(_test-output-stream) -1544 # . . push args -1545 68/push _test-output-stream/imm32 -1546 # . . call -1547 e8/call clear-stream/disp32 -1548 # . . discard args -1549 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1550 # . clear-stream(_test-output-buffered-file+4) -1551 # . . push args -1552 b8/copy-to-EAX _test-output-buffered-file/imm32 -1553 05/add-to-EAX 4/imm32 -1554 50/push-EAX -1555 # . . call -1556 e8/call clear-stream/disp32 -1557 # . . discard args -1558 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1559 # var slice/ECX = "/abc" -1560 b8/copy-to-EAX "/abc"/imm32 -1561 # . push end/ECX -1562 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1563 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -1564 51/push-ECX -1565 # . push curr/EAX -1566 05/add-to-EAX 4/imm32 -1567 50/push-EAX -1568 # . save stack pointer -1569 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1570 # emit-metadata(_test-output-buffered-file, slice) -1571 # . . push args -1572 51/push-ECX -1573 68/push _test-output-buffered-file/imm32 -1574 # . . call -1575 e8/call emit-metadata/disp32 -1576 # . . discard args -1577 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1578 # flush(_test-output-buffered-file) -1579 # . . push args -1580 68/push _test-output-buffered-file/imm32 -1581 # . . call -1582 e8/call flush/disp32 -1583 # . . discard args -1584 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1585 # check-stream-equal(_test-output-stream, "/abc", msg) # nothing skipped -1586 # . . push args -1587 68/push "F - test-emit-metadata-when-no-datum"/imm32 -1588 68/push "/abc"/imm32 -1589 68/push _test-output-stream/imm32 -1590 # . . call -1591 e8/call check-stream-equal/disp32 -1592 # . . discard args -1593 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1594 # . epilog -1595 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1596 5d/pop-to-EBP -1597 c3/return -1598 -1599 test-emit-metadata-in-string-literal: -1600 # . prolog -1601 55/push-EBP -1602 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1603 # setup -1604 # . clear-stream(_test-output-stream) -1605 # . . push args -1606 68/push _test-output-stream/imm32 -1607 # . . call -1608 e8/call clear-stream/disp32 -1609 # . . discard args -1610 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1611 # . clear-stream(_test-output-buffered-file+4) -1612 # . . push args -1613 b8/copy-to-EAX _test-output-buffered-file/imm32 -1614 05/add-to-EAX 4/imm32 -1615 50/push-EAX -1616 # . . call -1617 e8/call clear-stream/disp32 -1618 # . . discard args -1619 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1620 # var slice/ECX = "\"abc/def\"/ghi" -1621 68/push _test-slice-literal-string-with-metadata-end/imm32 -1622 68/push _test-slice-literal-string/imm32/start -1623 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1624 # emit-metadata(_test-output-buffered-file, slice) -1625 # . . push args -1626 51/push-ECX -1627 68/push _test-output-buffered-file/imm32 -1628 # . . call -1629 e8/call emit-metadata/disp32 -1630 # . . discard args -1631 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1632 # flush(_test-output-buffered-file) -1633 # . . push args -1634 68/push _test-output-buffered-file/imm32 -1635 # . . call -1636 e8/call flush/disp32 -1637 # . . discard args -1638 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1639 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1665 # check-stream-equal(_test-output-stream, "/ghi", msg) # important that there's no leading space -1666 # . . push args -1667 68/push "F - test-emit-metadata-in-string-literal"/imm32 -1668 68/push "/ghi"/imm32 -1669 68/push _test-output-stream/imm32 -1670 # . . call -1671 e8/call check-stream-equal/disp32 -1672 # . . discard args -1673 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1674 # . epilog -1675 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1676 5d/pop-to-EBP -1677 c3/return -1678 -1679 # (re)compute the bounds of the next word in the line -1680 # return empty string on reaching end of file -1681 next-word: # line : (address stream byte), out : (address slice) -1682 # . prolog -1683 55/push-EBP -1684 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1685 # . save registers -1686 50/push-EAX -1687 51/push-ECX -1688 56/push-ESI -1689 57/push-EDI -1690 # ESI = line -1691 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -1692 # EDI = out -1693 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -1694 # skip-chars-matching(line, ' ') -1695 # . . push args -1696 68/push 0x20/imm32/space -1697 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1698 # . . call -1699 e8/call skip-chars-matching/disp32 -1700 # . . discard args -1701 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1702 $next-word:check0: -1703 # if (line->read >= line->write) clear out and return -1704 # . EAX = line->read -1705 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX -1706 # . if (EAX < line->write) goto next check -1707 3b/compare 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # compare EAX with *ESI -1708 7c/jump-if-lesser $next-word:check-for-comment/disp8 -1709 # . return out = {0, 0} -1710 c7 0/subop/copy 0/mod/direct 7/rm32/EDI . . . . . 0/imm32 # copy to *EDI -1711 c7 0/subop/copy 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 0/imm32 # copy to *(EDI+4) -1712 eb/jump $next-word:end/disp8 -1713 $next-word:check-for-comment: -1714 # out->start = &line->data[line->read] -1715 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX -1716 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX -1717 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI -1718 # if line->data[line->read] == '#' -1719 # . EAX = line->data[line->read] -1720 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1721 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL -1722 # . compare -1723 3d/compare-EAX-and 0x23/imm32/pound -1724 75/jump-if-not-equal $next-word:check-for-string-literal/disp8 -1725 $next-word:comment: -1726 # out->end = &line->data[line->write] -1727 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX -1728 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX -1729 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -1730 # line->read = line->write # skip rest of line -1731 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX -1732 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) -1733 # return -1734 eb/jump $next-word:end/disp8 -1735 $next-word:check-for-string-literal: -1736 # if line->data[line->read] == '"' -1737 # . EAX = line->data[line->read] -1738 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1739 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL -1740 # . compare -1741 3d/compare-EAX-and 0x22/imm32/dquote -1742 75/jump-if-not-equal $next-word:regular-word/disp8 -1743 $next-word:string-literal: -1744 # skip-string(line) -1745 # . . push args -1746 56/push-ESI -1747 # . . call -1748 e8/call skip-string/disp32 -1749 # . . discard args -1750 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1751 # fall through -1752 $next-word:regular-word: -1753 # skip-chars-not-matching-whitespace(line) # including trailing newline -1754 # . . push args -1755 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1756 # . . call -1757 e8/call skip-chars-not-matching-whitespace/disp32 -1758 # . . discard args -1759 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1760 # out->end = &line->data[line->read] -1761 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX -1762 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX -1763 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -1764 $next-word:end: -1765 # . restore registers -1766 5f/pop-to-EDI -1767 5e/pop-to-ESI -1768 59/pop-to-ECX -1769 58/pop-to-EAX -1770 # . epilog -1771 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1772 5d/pop-to-EBP -1773 c3/return -1774 -1775 test-next-word: -1776 # . prolog -1777 55/push-EBP -1778 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1779 # setup -1780 # . clear-stream(_test-input-stream) -1781 # . . push args -1782 68/push _test-input-stream/imm32 -1783 # . . call -1784 e8/call clear-stream/disp32 -1785 # . . discard args -1786 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1787 # var slice/ECX = {0, 0} -1788 68/push 0/imm32/end -1789 68/push 0/imm32/start -1790 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1791 # write(_test-input-stream, " ab") -1792 # . . push args -1793 68/push " ab"/imm32 -1794 68/push _test-input-stream/imm32 -1795 # . . call -1796 e8/call write/disp32 -1797 # . . discard args -1798 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1799 # next-word(_test-input-stream, slice) -1800 # . . push args -1801 51/push-ECX -1802 68/push _test-input-stream/imm32 -1803 # . . call -1804 e8/call next-word/disp32 -1805 # . . discard args -1806 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1807 # check-ints-equal(_test-input-stream->read, 4, msg) -1808 # . . push args -1809 68/push "F - test-next-word/updates-stream-read-correctly"/imm32 -1810 68/push 4/imm32 -1811 b8/copy-to-EAX _test-input-stream/imm32 -1812 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +1458 50/push-EAX +1459 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1460 # emit-metadata(_test-output-buffered-file, slice) +1461 # . . push args +1462 51/push-ECX +1463 68/push _test-output-buffered-file/imm32 +1464 # . . call +1465 e8/call emit-metadata/disp32 +1466 # . . discard args +1467 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1468 # flush(_test-output-buffered-file) +1469 # . . push args +1470 68/push _test-output-buffered-file/imm32 +1471 # . . call +1472 e8/call flush/disp32 +1473 # . . discard args +1474 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1475 # check-stream-equal(_test-output-stream, "", msg) +1476 # . . push args +1477 68/push "F - test-emit-metadata-none"/imm32 +1478 68/push ""/imm32 +1479 68/push _test-output-stream/imm32 +1480 # . . call +1481 e8/call check-stream-equal/disp32 +1482 # . . discard args +1483 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1484 # . epilog +1485 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1486 5d/pop-to-EBP +1487 c3/return +1488 +1489 test-emit-metadata-multiple: +1490 # . prolog +1491 55/push-EBP +1492 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1493 # setup +1494 # . clear-stream(_test-output-stream) +1495 # . . push args +1496 68/push _test-output-stream/imm32 +1497 # . . call +1498 e8/call clear-stream/disp32 +1499 # . . discard args +1500 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1501 # . clear-stream(_test-output-buffered-file+4) +1502 # . . push args +1503 b8/copy-to-EAX _test-output-buffered-file/imm32 +1504 05/add-to-EAX 4/imm32 +1505 50/push-EAX +1506 # . . call +1507 e8/call clear-stream/disp32 +1508 # . . discard args +1509 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1510 # (EAX..ECX) = "abc/def/ghi" +1511 b8/copy-to-EAX "abc/def/ghi"/imm32 +1512 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1513 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +1514 05/add-to-EAX 4/imm32 +1515 # var slice/ECX = {EAX, ECX} +1516 51/push-ECX +1517 50/push-EAX +1518 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1519 # emit-metadata(_test-output-buffered-file, slice) +1520 # . . push args +1521 51/push-ECX +1522 68/push _test-output-buffered-file/imm32 +1523 # . . call +1524 e8/call emit-metadata/disp32 +1525 # . . discard args +1526 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1527 # flush(_test-output-buffered-file) +1528 # . . push args +1529 68/push _test-output-buffered-file/imm32 +1530 # . . call +1531 e8/call flush/disp32 +1532 # . . discard args +1533 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1534 # check-stream-equal(_test-output-stream, "/def/ghi", msg) # important that there's no leading space +1535 # . . push args +1536 68/push "F - test-emit-metadata-multiple"/imm32 +1537 68/push "/def/ghi"/imm32 +1538 68/push _test-output-stream/imm32 +1539 # . . call +1540 e8/call check-stream-equal/disp32 +1541 # . . discard args +1542 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1543 # . epilog +1544 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1545 5d/pop-to-EBP +1546 c3/return +1547 +1548 test-emit-metadata-when-no-datum: +1549 # . prolog +1550 55/push-EBP +1551 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1552 # setup +1553 # . clear-stream(_test-output-stream) +1554 # . . push args +1555 68/push _test-output-stream/imm32 +1556 # . . call +1557 e8/call clear-stream/disp32 +1558 # . . discard args +1559 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1560 # . clear-stream(_test-output-buffered-file+4) +1561 # . . push args +1562 b8/copy-to-EAX _test-output-buffered-file/imm32 +1563 05/add-to-EAX 4/imm32 +1564 50/push-EAX +1565 # . . call +1566 e8/call clear-stream/disp32 +1567 # . . discard args +1568 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1569 # var slice/ECX = "/abc" +1570 b8/copy-to-EAX "/abc"/imm32 +1571 # . push end/ECX +1572 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1573 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +1574 51/push-ECX +1575 # . push curr/EAX +1576 05/add-to-EAX 4/imm32 +1577 50/push-EAX +1578 # . save stack pointer +1579 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1580 # emit-metadata(_test-output-buffered-file, slice) +1581 # . . push args +1582 51/push-ECX +1583 68/push _test-output-buffered-file/imm32 +1584 # . . call +1585 e8/call emit-metadata/disp32 +1586 # . . discard args +1587 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1588 # flush(_test-output-buffered-file) +1589 # . . push args +1590 68/push _test-output-buffered-file/imm32 +1591 # . . call +1592 e8/call flush/disp32 +1593 # . . discard args +1594 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1595 # check-stream-equal(_test-output-stream, "/abc", msg) # nothing skipped +1596 # . . push args +1597 68/push "F - test-emit-metadata-when-no-datum"/imm32 +1598 68/push "/abc"/imm32 +1599 68/push _test-output-stream/imm32 +1600 # . . call +1601 e8/call check-stream-equal/disp32 +1602 # . . discard args +1603 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1604 # . epilog +1605 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1606 5d/pop-to-EBP +1607 c3/return +1608 +1609 test-emit-metadata-in-string-literal: +1610 # . prolog +1611 55/push-EBP +1612 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1613 # setup +1614 # . clear-stream(_test-output-stream) +1615 # . . push args +1616 68/push _test-output-stream/imm32 +1617 # . . call +1618 e8/call clear-stream/disp32 +1619 # . . discard args +1620 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1621 # . clear-stream(_test-output-buffered-file+4) +1622 # . . push args +1623 b8/copy-to-EAX _test-output-buffered-file/imm32 +1624 05/add-to-EAX 4/imm32 +1625 50/push-EAX +1626 # . . call +1627 e8/call clear-stream/disp32 +1628 # . . discard args +1629 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1630 # var slice/ECX = "\"abc/def\"/ghi" +1631 68/push _test-slice-literal-string-with-limit/imm32 +1632 68/push _test-slice-literal-string/imm32/start +1633 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1634 # emit-metadata(_test-output-buffered-file, slice) +1635 # . . push args +1636 51/push-ECX +1637 68/push _test-output-buffered-file/imm32 +1638 # . . call +1639 e8/call emit-metadata/disp32 +1640 # . . discard args +1641 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1642 # flush(_test-output-buffered-file) +1643 # . . push args +1644 68/push _test-output-buffered-file/imm32 +1645 # . . call +1646 e8/call flush/disp32 +1647 # . . discard args +1648 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1649 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1675 # check-stream-equal(_test-output-stream, "/ghi", msg) # important that there's no leading space +1676 # . . push args +1677 68/push "F - test-emit-metadata-in-string-literal"/imm32 +1678 68/push "/ghi"/imm32 +1679 68/push _test-output-stream/imm32 +1680 # . . call +1681 e8/call check-stream-equal/disp32 +1682 # . . discard args +1683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1684 # . epilog +1685 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1686 5d/pop-to-EBP +1687 c3/return +1688 +1689 # (re)compute the bounds of the next word in the line +1690 # return empty string on reaching end of file +1691 next-word-or-string: # line : (address stream byte), out : (address slice) +1692 # . prolog +1693 55/push-EBP +1694 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1695 # . save registers +1696 50/push-EAX +1697 51/push-ECX +1698 56/push-ESI +1699 57/push-EDI +1700 # ESI = line +1701 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +1702 # EDI = out +1703 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI +1704 # skip-chars-matching(line, ' ') +1705 # . . push args +1706 68/push 0x20/imm32/space +1707 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1708 # . . call +1709 e8/call skip-chars-matching/disp32 +1710 # . . discard args +1711 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1712 $next-word-or-string:check0: +1713 # if (line->read >= line->write) clear out and return +1714 # . EAX = line->read +1715 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX +1716 # . if (EAX < line->write) goto next check +1717 3b/compare 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # compare EAX with *ESI +1718 7c/jump-if-lesser $next-word-or-string:check-for-comment/disp8 +1719 # . return out = {0, 0} +1720 c7 0/subop/copy 0/mod/direct 7/rm32/EDI . . . . . 0/imm32 # copy to *EDI +1721 c7 0/subop/copy 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 0/imm32 # copy to *(EDI+4) +1722 eb/jump $next-word-or-string:end/disp8 +1723 $next-word-or-string:check-for-comment: +1724 # out->start = &line->data[line->read] +1725 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX +1726 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX +1727 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI +1728 # if line->data[line->read] == '#' +1729 # . EAX = line->data[line->read] +1730 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +1731 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL +1732 # . compare +1733 3d/compare-EAX-and 0x23/imm32/pound +1734 75/jump-if-not-equal $next-word-or-string:check-for-string-literal/disp8 +1735 $next-word-or-string:comment: +1736 # out->end = &line->data[line->write] +1737 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX +1738 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX +1739 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) +1740 # line->read = line->write # skip rest of line +1741 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX +1742 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) +1743 # return +1744 eb/jump $next-word-or-string:end/disp8 +1745 $next-word-or-string:check-for-string-literal: +1746 # if line->data[line->read] == '"' +1747 # . EAX = line->data[line->read] +1748 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +1749 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL +1750 # . compare +1751 3d/compare-EAX-and 0x22/imm32/dquote +1752 75/jump-if-not-equal $next-word-or-string:regular-word/disp8 +1753 $next-word-or-string:string-literal: +1754 # skip-string(line) +1755 # . . push args +1756 56/push-ESI +1757 # . . call +1758 e8/call skip-string/disp32 +1759 # . . discard args +1760 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1761 # fall through +1762 $next-word-or-string:regular-word: +1763 # skip-chars-not-matching-whitespace(line) # including trailing newline +1764 # . . push args +1765 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1766 # . . call +1767 e8/call skip-chars-not-matching-whitespace/disp32 +1768 # . . discard args +1769 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1770 # out->end = &line->data[line->read] +1771 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX +1772 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX +1773 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) +1774 $next-word-or-string:end: +1775 # . restore registers +1776 5f/pop-to-EDI +1777 5e/pop-to-ESI +1778 59/pop-to-ECX +1779 58/pop-to-EAX +1780 # . epilog +1781 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1782 5d/pop-to-EBP +1783 c3/return +1784 +1785 test-next-word-or-string: +1786 # . prolog +1787 55/push-EBP +1788 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1789 # setup +1790 # . clear-stream(_test-input-stream) +1791 # . . push args +1792 68/push _test-input-stream/imm32 +1793 # . . call +1794 e8/call clear-stream/disp32 +1795 # . . discard args +1796 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1797 # var slice/ECX = {0, 0} +1798 68/push 0/imm32/end +1799 68/push 0/imm32/start +1800 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1801 # write(_test-input-stream, " ab") +1802 # . . push args +1803 68/push " ab"/imm32 +1804 68/push _test-input-stream/imm32 +1805 # . . call +1806 e8/call write/disp32 +1807 # . . discard args +1808 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1809 # next-word-or-string(_test-input-stream, slice) +1810 # . . push args +1811 51/push-ECX +1812 68/push _test-input-stream/imm32 1813 # . . call -1814 e8/call check-ints-equal/disp32 +1814 e8/call next-word-or-string/disp32 1815 # . . discard args -1816 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1817 # check-ints-equal(slice->start - _test-input-stream->data, 2, msg) -1818 # . check-ints-equal(slice->start - _test-input-stream, 14, msg) -1819 # . . push args -1820 68/push "F - test-next-word: start"/imm32 -1821 68/push 0xe/imm32 -1822 # . . push slice->start - _test-input-stream -1823 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -1824 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -1825 50/push-EAX -1826 # . . call -1827 e8/call check-ints-equal/disp32 -1828 # . . discard args -1829 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1830 # check-ints-equal(slice->end - _test-input-stream->data, 4, msg) -1831 # . check-ints-equal(slice->end - _test-input-stream, 16, msg) -1832 # . . push args -1833 68/push "F - test-next-word: end"/imm32 -1834 68/push 0x10/imm32 -1835 # . . push slice->end - _test-input-stream -1836 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -1837 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -1838 50/push-EAX -1839 # . . call -1840 e8/call check-ints-equal/disp32 -1841 # . . discard args -1842 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1843 # . epilog -1844 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1845 5d/pop-to-EBP -1846 c3/return -1847 -1848 test-next-word-returns-whole-comment: -1849 # . prolog -1850 55/push-EBP -1851 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1852 # setup -1853 # . clear-stream(_test-input-stream) -1854 # . . push args -1855 68/push _test-input-stream/imm32 -1856 # . . call -1857 e8/call clear-stream/disp32 -1858 # . . discard args -1859 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1860 # var slice/ECX = {0, 0} -1861 68/push 0/imm32/end -1862 68/push 0/imm32/start -1863 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1864 # write(_test-input-stream, " # a") -1865 # . . push args -1866 68/push " # a"/imm32 -1867 68/push _test-input-stream/imm32 -1868 # . . call -1869 e8/call write/disp32 -1870 # . . discard args -1871 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1872 # next-word(_test-input-stream, slice) -1873 # . . push args -1874 51/push-ECX -1875 68/push _test-input-stream/imm32 -1876 # . . call -1877 e8/call next-word/disp32 -1878 # . . discard args -1879 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1880 # check-ints-equal(_test-input-stream->read, 5, msg) -1881 # . . push args -1882 68/push "F - test-next-word-returns-whole-comment/updates-stream-read-correctly"/imm32 -1883 68/push 5/imm32 -1884 b8/copy-to-EAX _test-input-stream/imm32 -1885 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +1816 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1817 # check-ints-equal(_test-input-stream->read, 4, msg) +1818 # . . push args +1819 68/push "F - test-next-word-or-string/updates-stream-read-correctly"/imm32 +1820 68/push 4/imm32 +1821 b8/copy-to-EAX _test-input-stream/imm32 +1822 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +1823 # . . call +1824 e8/call check-ints-equal/disp32 +1825 # . . discard args +1826 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1827 # check-ints-equal(slice->start - _test-input-stream->data, 2, msg) +1828 # . check-ints-equal(slice->start - _test-input-stream, 14, msg) +1829 # . . push args +1830 68/push "F - test-next-word-or-string: start"/imm32 +1831 68/push 0xe/imm32 +1832 # . . push slice->start - _test-input-stream +1833 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX +1834 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +1835 50/push-EAX +1836 # . . call +1837 e8/call check-ints-equal/disp32 +1838 # . . discard args +1839 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1840 # check-ints-equal(slice->end - _test-input-stream->data, 4, msg) +1841 # . check-ints-equal(slice->end - _test-input-stream, 16, msg) +1842 # . . push args +1843 68/push "F - test-next-word-or-string: end"/imm32 +1844 68/push 0x10/imm32 +1845 # . . push slice->end - _test-input-stream +1846 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX +1847 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +1848 50/push-EAX +1849 # . . call +1850 e8/call check-ints-equal/disp32 +1851 # . . discard args +1852 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1853 # . epilog +1854 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1855 5d/pop-to-EBP +1856 c3/return +1857 +1858 test-next-word-or-string-returns-whole-comment: +1859 # . prolog +1860 55/push-EBP +1861 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1862 # setup +1863 # . clear-stream(_test-input-stream) +1864 # . . push args +1865 68/push _test-input-stream/imm32 +1866 # . . call +1867 e8/call clear-stream/disp32 +1868 # . . discard args +1869 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1870 # var slice/ECX = {0, 0} +1871 68/push 0/imm32/end +1872 68/push 0/imm32/start +1873 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1874 # write(_test-input-stream, " # a") +1875 # . . push args +1876 68/push " # a"/imm32 +1877 68/push _test-input-stream/imm32 +1878 # . . call +1879 e8/call write/disp32 +1880 # . . discard args +1881 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1882 # next-word-or-string(_test-input-stream, slice) +1883 # . . push args +1884 51/push-ECX +1885 68/push _test-input-stream/imm32 1886 # . . call -1887 e8/call check-ints-equal/disp32 +1887 e8/call next-word-or-string/disp32 1888 # . . discard args -1889 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1890 # check-ints-equal(slice->start - _test-input-stream->data, 2, msg) -1891 # . check-ints-equal(slice->start - _test-input-stream, 14, msg) -1892 # . . push args -1893 68/push "F - test-next-word-returns-whole-comment: start"/imm32 -1894 68/push 0xe/imm32 -1895 # . . push slice->start - _test-input-stream -1896 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -1897 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -1898 50/push-EAX -1899 # . . call -1900 e8/call check-ints-equal/disp32 -1901 # . . discard args -1902 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1903 # check-ints-equal(slice->end - _test-input-stream->data, 5, msg) -1904 # . check-ints-equal(slice->end - _test-input-stream, 17, msg) -1905 # . . push args -1906 68/push "F - test-next-word-returns-whole-comment: end"/imm32 -1907 68/push 0x11/imm32 -1908 # . . push slice->end - _test-input-stream -1909 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -1910 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -1911 50/push-EAX -1912 # . . call -1913 e8/call check-ints-equal/disp32 -1914 # . . discard args -1915 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1916 # . epilog -1917 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1918 5d/pop-to-EBP -1919 c3/return -1920 -1921 test-next-word-returns-empty-string-on-eof: -1922 # . prolog -1923 55/push-EBP -1924 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1925 # setup -1926 # . clear-stream(_test-input-stream) -1927 # . . push args -1928 68/push _test-input-stream/imm32 -1929 # . . call -1930 e8/call clear-stream/disp32 -1931 # . . discard args -1932 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1933 # var slice/ECX = {0, 0} -1934 68/push 0/imm32/end -1935 68/push 0/imm32/start -1936 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1937 # write nothing to _test-input-stream -1938 # next-word(_test-input-stream, slice) -1939 # . . push args -1940 51/push-ECX -1941 68/push _test-input-stream/imm32 -1942 # . . call -1943 e8/call next-word/disp32 -1944 # . . discard args -1945 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1946 # check-ints-equal(slice->end - slice->start, 0, msg) -1947 # . . push args -1948 68/push "F - test-next-word-returns-empty-string-on-eof"/imm32 -1949 68/push 0/imm32 -1950 # . . push slice->end - slice->start -1951 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -1952 2b/subtract 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # subtract *ECX from EAX -1953 50/push-EAX -1954 # . . call -1955 e8/call check-ints-equal/disp32 -1956 # . . discard args -1957 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1958 # . epilog -1959 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1960 5d/pop-to-EBP -1961 c3/return -1962 -1963 test-next-word-returns-whole-string: -1964 # . prolog -1965 55/push-EBP -1966 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1967 # setup -1968 # . clear-stream(_test-input-stream) -1969 # . . push args -1970 68/push _test-input-stream/imm32 -1971 # . . call -1972 e8/call clear-stream/disp32 -1973 # . . discard args -1974 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1975 # var slice/ECX = {0, 0} -1976 68/push 0/imm32/end -1977 68/push 0/imm32/start -1978 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1979 # write(_test-input-stream, " \"a b\"/imm32 ") -1980 # . . push args -1981 68/push " \"a b\"/imm32 "/imm32 -1982 68/push _test-input-stream/imm32 -1983 # . . call -1984 e8/call write/disp32 -1985 # . . discard args -1986 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1987 # next-word(_test-input-stream, slice) -1988 # . . push args -1989 51/push-ECX -1990 68/push _test-input-stream/imm32 -1991 # . . call -1992 e8/call next-word/disp32 -1993 # . . discard args -1994 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1995 # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) -1996 # . check-ints-equal(slice->start - _test-input-stream, 13, msg) -1997 # . . push args -1998 68/push "F - test-next-word-returns-whole-string: start"/imm32 -1999 68/push 0xd/imm32 -2000 # . . push slice->start - _test-input-stream -2001 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -2002 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -2003 50/push-EAX -2004 # . . call -2005 e8/call check-ints-equal/disp32 -2006 # . . discard args -2007 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2008 # check-ints-equal(slice->end - _test-input-stream->data, 12, msg) -2009 # . check-ints-equal(slice->end - _test-input-stream, 24, msg) -2010 # . . push args -2011 68/push "F - test-next-word-returns-whole-string: end"/imm32 -2012 68/push 0x18/imm32 -2013 # . . push slice->end - _test-input-stream -2014 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -2015 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -2016 50/push-EAX -2017 # . . call -2018 e8/call check-ints-equal/disp32 -2019 # . . discard args -2020 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2021 # . epilog -2022 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2023 5d/pop-to-EBP -2024 c3/return -2025 -2026 test-next-word-returns-string-with-escapes: -2027 # . prolog -2028 55/push-EBP -2029 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2030 # setup -2031 # . clear-stream(_test-input-stream) -2032 # . . push args -2033 68/push _test-input-stream/imm32 -2034 # . . call -2035 e8/call clear-stream/disp32 -2036 # . . discard args -2037 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2038 # var slice/ECX = {0, 0} -2039 68/push 0/imm32/end -2040 68/push 0/imm32/start -2041 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -2042 # write(_test-input-stream, " \"a\\\"b\"/x") -2043 # . . push args -2044 68/push " \"a\\\"b\"/x"/imm32 -2045 68/push _test-input-stream/imm32 -2046 # . . call -2047 e8/call write/disp32 -2048 # . . discard args -2049 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2050 # next-word(_test-input-stream, slice) -2051 # . . push args -2052 51/push-ECX -2053 68/push _test-input-stream/imm32 -2054 # . . call -2055 e8/call next-word/disp32 -2056 # . . discard args -2057 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2058 # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) -2059 # . check-ints-equal(slice->start - _test-input-stream, 13, msg) -2060 # . . push args -2061 68/push "F - test-next-word-returns-string-with-escapes: start"/imm32 -2062 68/push 0xd/imm32 -2063 # . . push slice->start - _test-input-stream -2064 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -2065 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -2066 50/push-EAX -2067 # . . call -2068 e8/call check-ints-equal/disp32 -2069 # . . discard args -2070 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2071 # check-ints-equal(slice->end - _test-input-stream->data, 9, msg) -2072 # . check-ints-equal(slice->end - _test-input-stream, 21, msg) -2073 # . . push args -2074 68/push "F - test-next-word-returns-string-with-escapes: end"/imm32 -2075 68/push 0x15/imm32 -2076 # . . push slice->end - _test-input-stream -2077 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -2078 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX -2079 50/push-EAX -2080 # . . call -2081 e8/call check-ints-equal/disp32 -2082 # . . discard args -2083 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2084 # . epilog -2085 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2086 5d/pop-to-EBP -2087 c3/return -2088 -2089 # update line->read to end of string literal surrounded by double quotes -2090 # line->read must start out at a double-quote -2091 skip-string: # line : (address stream) -2092 # . prolog -2093 55/push-EBP -2094 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2095 # . save registers -2096 50/push-EAX -2097 51/push-ECX -2098 52/push-EDX -2099 # ECX = line -2100 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -2101 # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) -2102 # . . push &line->data[line->write] -2103 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 8/disp8 . # copy *(ECX+8) to EDX -2104 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX -2105 52/push-EDX -2106 # . . push &line->data[line->read] -2107 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX -2108 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX -2109 52/push-EDX -2110 # . . call -2111 e8/call skip-string-in-slice/disp32 -2112 # . . discard args -2113 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2114 # line->read = EAX - line->data -2115 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX -2116 2d/subtract-from-EAX 0xc/imm32 -2117 89/copy 1/mod/*+disp8 1/rm32/ECX . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4) -2118 $skip-string:end: -2119 # . restore registers -2120 5a/pop-to-EDX -2121 59/pop-to-ECX -2122 58/pop-to-EAX -2123 # . epilog -2124 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2125 5d/pop-to-EBP -2126 c3/return -2127 -2128 test-skip-string: -2129 # . prolog -2130 55/push-EBP -2131 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2132 # setup -2133 # . clear-stream(_test-input-stream) -2134 # . . push args -2135 68/push _test-input-stream/imm32 -2136 # . . call -2137 e8/call clear-stream/disp32 -2138 # . . discard args -2139 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2140 # . write(_test-input-stream, "\"abc\" def") -2141 # . indices: 0123 45 -2142 # . . push args -2143 68/push "\"abc\" def"/imm32 -2144 68/push _test-input-stream/imm32 -2145 # . . call -2146 e8/call write/disp32 -2147 # . . discard args -2148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2149 # precondition: line->read == 0 -2150 # . . push args -2151 68/push "F - test-skip-string/precondition"/imm32 -2152 68/push 0/imm32 -2153 b8/copy-to-EAX _test-input-stream/imm32 -2154 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +1889 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1890 # check-ints-equal(_test-input-stream->read, 5, msg) +1891 # . . push args +1892 68/push "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32 +1893 68/push 5/imm32 +1894 b8/copy-to-EAX _test-input-stream/imm32 +1895 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +1896 # . . call +1897 e8/call check-ints-equal/disp32 +1898 # . . discard args +1899 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1900 # check-ints-equal(slice->start - _test-input-stream->data, 2, msg) +1901 # . check-ints-equal(slice->start - _test-input-stream, 14, msg) +1902 # . . push args +1903 68/push "F - test-next-word-or-string-returns-whole-comment: start"/imm32 +1904 68/push 0xe/imm32 +1905 # . . push slice->start - _test-input-stream +1906 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX +1907 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +1908 50/push-EAX +1909 # . . call +1910 e8/call check-ints-equal/disp32 +1911 # . . discard args +1912 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1913 # check-ints-equal(slice->end - _test-input-stream->data, 5, msg) +1914 # . check-ints-equal(slice->end - _test-input-stream, 17, msg) +1915 # . . push args +1916 68/push "F - test-next-word-or-string-returns-whole-comment: end"/imm32 +1917 68/push 0x11/imm32 +1918 # . . push slice->end - _test-input-stream +1919 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX +1920 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +1921 50/push-EAX +1922 # . . call +1923 e8/call check-ints-equal/disp32 +1924 # . . discard args +1925 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1926 # . epilog +1927 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1928 5d/pop-to-EBP +1929 c3/return +1930 +1931 test-next-word-or-string-returns-empty-string-on-eof: +1932 # . prolog +1933 55/push-EBP +1934 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1935 # setup +1936 # . clear-stream(_test-input-stream) +1937 # . . push args +1938 68/push _test-input-stream/imm32 +1939 # . . call +1940 e8/call clear-stream/disp32 +1941 # . . discard args +1942 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1943 # var slice/ECX = {0, 0} +1944 68/push 0/imm32/end +1945 68/push 0/imm32/start +1946 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1947 # write nothing to _test-input-stream +1948 # next-word-or-string(_test-input-stream, slice) +1949 # . . push args +1950 51/push-ECX +1951 68/push _test-input-stream/imm32 +1952 # . . call +1953 e8/call next-word-or-string/disp32 +1954 # . . discard args +1955 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1956 # check-ints-equal(slice->end - slice->start, 0, msg) +1957 # . . push args +1958 68/push "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32 +1959 68/push 0/imm32 +1960 # . . push slice->end - slice->start +1961 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX +1962 2b/subtract 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # subtract *ECX from EAX +1963 50/push-EAX +1964 # . . call +1965 e8/call check-ints-equal/disp32 +1966 # . . discard args +1967 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1968 # . epilog +1969 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1970 5d/pop-to-EBP +1971 c3/return +1972 +1973 test-next-word-or-string-returns-whole-string: +1974 # . prolog +1975 55/push-EBP +1976 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1977 # setup +1978 # . clear-stream(_test-input-stream) +1979 # . . push args +1980 68/push _test-input-stream/imm32 +1981 # . . call +1982 e8/call clear-stream/disp32 +1983 # . . discard args +1984 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1985 # var slice/ECX = {0, 0} +1986 68/push 0/imm32/end +1987 68/push 0/imm32/start +1988 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1989 # write(_test-input-stream, " \"a b\"/imm32 ") +1990 # . . push args +1991 68/push " \"a b\"/imm32 "/imm32 +1992 68/push _test-input-stream/imm32 +1993 # . . call +1994 e8/call write/disp32 +1995 # . . discard args +1996 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1997 # next-word-or-string(_test-input-stream, slice) +1998 # . . push args +1999 51/push-ECX +2000 68/push _test-input-stream/imm32 +2001 # . . call +2002 e8/call next-word-or-string/disp32 +2003 # . . discard args +2004 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2005 # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) +2006 # . check-ints-equal(slice->start - _test-input-stream, 13, msg) +2007 # . . push args +2008 68/push "F - test-next-word-or-string-returns-whole-string: start"/imm32 +2009 68/push 0xd/imm32 +2010 # . . push slice->start - _test-input-stream +2011 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX +2012 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +2013 50/push-EAX +2014 # . . call +2015 e8/call check-ints-equal/disp32 +2016 # . . discard args +2017 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2018 # check-ints-equal(slice->end - _test-input-stream->data, 12, msg) +2019 # . check-ints-equal(slice->end - _test-input-stream, 24, msg) +2020 # . . push args +2021 68/push "F - test-next-word-or-string-returns-whole-string: end"/imm32 +2022 68/push 0x18/imm32 +2023 # . . push slice->end - _test-input-stream +2024 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX +2025 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +2026 50/push-EAX +2027 # . . call +2028 e8/call check-ints-equal/disp32 +2029 # . . discard args +2030 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2031 # . epilog +2032 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2033 5d/pop-to-EBP +2034 c3/return +2035 +2036 test-next-word-or-string-returns-string-with-escapes: +2037 # . prolog +2038 55/push-EBP +2039 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2040 # setup +2041 # . clear-stream(_test-input-stream) +2042 # . . push args +2043 68/push _test-input-stream/imm32 +2044 # . . call +2045 e8/call clear-stream/disp32 +2046 # . . discard args +2047 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2048 # var slice/ECX = {0, 0} +2049 68/push 0/imm32/end +2050 68/push 0/imm32/start +2051 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2052 # write(_test-input-stream, " \"a\\\"b\"/x") +2053 # . . push args +2054 68/push " \"a\\\"b\"/x"/imm32 +2055 68/push _test-input-stream/imm32 +2056 # . . call +2057 e8/call write/disp32 +2058 # . . discard args +2059 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2060 # next-word-or-string(_test-input-stream, slice) +2061 # . . push args +2062 51/push-ECX +2063 68/push _test-input-stream/imm32 +2064 # . . call +2065 e8/call next-word-or-string/disp32 +2066 # . . discard args +2067 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2068 # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) +2069 # . check-ints-equal(slice->start - _test-input-stream, 13, msg) +2070 # . . push args +2071 68/push "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32 +2072 68/push 0xd/imm32 +2073 # . . push slice->start - _test-input-stream +2074 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX +2075 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +2076 50/push-EAX +2077 # . . call +2078 e8/call check-ints-equal/disp32 +2079 # . . discard args +2080 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2081 # check-ints-equal(slice->end - _test-input-stream->data, 9, msg) +2082 # . check-ints-equal(slice->end - _test-input-stream, 21, msg) +2083 # . . push args +2084 68/push "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32 +2085 68/push 0x15/imm32 +2086 # . . push slice->end - _test-input-stream +2087 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX +2088 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX +2089 50/push-EAX +2090 # . . call +2091 e8/call check-ints-equal/disp32 +2092 # . . discard args +2093 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2094 # . epilog +2095 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2096 5d/pop-to-EBP +2097 c3/return +2098 +2099 # update line->read to end of string literal surrounded by double quotes +2100 # line->read must start out at a double-quote +2101 skip-string: # line : (address stream) +2102 # . prolog +2103 55/push-EBP +2104 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2105 # . save registers +2106 50/push-EAX +2107 51/push-ECX +2108 52/push-EDX +2109 # ECX = line +2110 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +2111 # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) +2112 # . . push &line->data[line->write] +2113 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 8/disp8 . # copy *(ECX+8) to EDX +2114 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX +2115 52/push-EDX +2116 # . . push &line->data[line->read] +2117 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX +2118 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX +2119 52/push-EDX +2120 # . . call +2121 e8/call skip-string-in-slice/disp32 +2122 # . . discard args +2123 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2124 # line->read = EAX - line->data +2125 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX +2126 2d/subtract-from-EAX 0xc/imm32 +2127 89/copy 1/mod/*+disp8 1/rm32/ECX . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4) +2128 $skip-string:end: +2129 # . restore registers +2130 5a/pop-to-EDX +2131 59/pop-to-ECX +2132 58/pop-to-EAX +2133 # . epilog +2134 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2135 5d/pop-to-EBP +2136 c3/return +2137 +2138 test-skip-string: +2139 # . prolog +2140 55/push-EBP +2141 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2142 # setup +2143 # . clear-stream(_test-input-stream) +2144 # . . push args +2145 68/push _test-input-stream/imm32 +2146 # . . call +2147 e8/call clear-stream/disp32 +2148 # . . discard args +2149 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2150 # . write(_test-input-stream, "\"abc\" def") +2151 # . indices: 0123 45 +2152 # . . push args +2153 68/push "\"abc\" def"/imm32 +2154 68/push _test-input-stream/imm32 2155 # . . call -2156 e8/call check-ints-equal/disp32 +2156 e8/call write/disp32 2157 # . . discard args -2158 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2159 # skip-string(_test-input-stream) +2158 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2159 # precondition: line->read == 0 2160 # . . push args -2161 68/push _test-input-stream/imm32 -2162 # . . call -2163 e8/call skip-string/disp32 -2164 # . . discard args -2165 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2166 # check-ints-equal(line->read, 5, msg) -2167 # . . push args -2168 68/push "F - test-skip-string"/imm32 -2169 68/push 5/imm32 -2170 b8/copy-to-EAX _test-input-stream/imm32 -2171 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2161 68/push "F - test-skip-string/precondition"/imm32 +2162 68/push 0/imm32 +2163 b8/copy-to-EAX _test-input-stream/imm32 +2164 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2165 # . . call +2166 e8/call check-ints-equal/disp32 +2167 # . . discard args +2168 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2169 # skip-string(_test-input-stream) +2170 # . . push args +2171 68/push _test-input-stream/imm32 2172 # . . call -2173 e8/call check-ints-equal/disp32 +2173 e8/call skip-string/disp32 2174 # . . discard args -2175 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2176 # . epilog -2177 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2178 5d/pop-to-EBP -2179 c3/return -2180 -2181 test-skip-string-ignores-spaces: -2182 # . prolog -2183 55/push-EBP -2184 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2185 # setup -2186 # . clear-stream(_test-input-stream) -2187 # . . push args -2188 68/push _test-input-stream/imm32 -2189 # . . call -2190 e8/call clear-stream/disp32 -2191 # . . discard args -2192 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2193 # . write(_test-input-stream, "\"a b\"/yz") -2194 # . indices: 0123 45 -2195 # . . push args -2196 68/push "\"a b\"/yz"/imm32 -2197 68/push _test-input-stream/imm32 -2198 # . . call -2199 e8/call write/disp32 -2200 # . . discard args -2201 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2202 # precondition: line->read == 0 -2203 # . . push args -2204 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 -2205 68/push 0/imm32 -2206 b8/copy-to-EAX _test-input-stream/imm32 -2207 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2175 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2176 # check-ints-equal(line->read, 5, msg) +2177 # . . push args +2178 68/push "F - test-skip-string"/imm32 +2179 68/push 5/imm32 +2180 b8/copy-to-EAX _test-input-stream/imm32 +2181 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2182 # . . call +2183 e8/call check-ints-equal/disp32 +2184 # . . discard args +2185 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2186 # . epilog +2187 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2188 5d/pop-to-EBP +2189 c3/return +2190 +2191 test-skip-string-ignores-spaces: +2192 # . prolog +2193 55/push-EBP +2194 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2195 # setup +2196 # . clear-stream(_test-input-stream) +2197 # . . push args +2198 68/push _test-input-stream/imm32 +2199 # . . call +2200 e8/call clear-stream/disp32 +2201 # . . discard args +2202 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2203 # . write(_test-input-stream, "\"a b\"/yz") +2204 # . indices: 0123 45 +2205 # . . push args +2206 68/push "\"a b\"/yz"/imm32 +2207 68/push _test-input-stream/imm32 2208 # . . call -2209 e8/call check-ints-equal/disp32 +2209 e8/call write/disp32 2210 # . . discard args -2211 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2212 # skip-string(_test-input-stream) +2211 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2212 # precondition: line->read == 0 2213 # . . push args -2214 68/push _test-input-stream/imm32 -2215 # . . call -2216 e8/call skip-string/disp32 -2217 # . . discard args -2218 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2219 # check-ints-equal(line->read, 5, msg) -2220 # . . push args -2221 68/push "F - test-skip-string-ignores-spaces"/imm32 -2222 68/push 5/imm32 -2223 b8/copy-to-EAX _test-input-stream/imm32 -2224 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2214 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 +2215 68/push 0/imm32 +2216 b8/copy-to-EAX _test-input-stream/imm32 +2217 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2218 # . . call +2219 e8/call check-ints-equal/disp32 +2220 # . . discard args +2221 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2222 # skip-string(_test-input-stream) +2223 # . . push args +2224 68/push _test-input-stream/imm32 2225 # . . call -2226 e8/call check-ints-equal/disp32 +2226 e8/call skip-string/disp32 2227 # . . discard args -2228 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2229 # . epilog -2230 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2231 5d/pop-to-EBP -2232 c3/return -2233 -2234 test-skip-string-ignores-escapes: -2235 # . prolog -2236 55/push-EBP -2237 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2238 # setup -2239 # . clear-stream(_test-input-stream) -2240 # . . push args -2241 68/push _test-input-stream/imm32 -2242 # . . call -2243 e8/call clear-stream/disp32 -2244 # . . discard args -2245 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2246 # . write(_test-input-stream, "\"a\\\"b\"/yz") -2247 # . indices: 01 2 34 56 -2248 # . . push args -2249 68/push "\"a\\\"b\"/yz"/imm32 -2250 68/push _test-input-stream/imm32 -2251 # . . call -2252 e8/call write/disp32 -2253 # . . discard args -2254 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2255 # precondition: line->read == 0 -2256 # . . push args -2257 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 -2258 68/push 0/imm32 -2259 b8/copy-to-EAX _test-input-stream/imm32 -2260 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2228 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2229 # check-ints-equal(line->read, 5, msg) +2230 # . . push args +2231 68/push "F - test-skip-string-ignores-spaces"/imm32 +2232 68/push 5/imm32 +2233 b8/copy-to-EAX _test-input-stream/imm32 +2234 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2235 # . . call +2236 e8/call check-ints-equal/disp32 +2237 # . . discard args +2238 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2239 # . epilog +2240 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2241 5d/pop-to-EBP +2242 c3/return +2243 +2244 test-skip-string-ignores-escapes: +2245 # . prolog +2246 55/push-EBP +2247 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2248 # setup +2249 # . clear-stream(_test-input-stream) +2250 # . . push args +2251 68/push _test-input-stream/imm32 +2252 # . . call +2253 e8/call clear-stream/disp32 +2254 # . . discard args +2255 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2256 # . write(_test-input-stream, "\"a\\\"b\"/yz") +2257 # . indices: 01 2 34 56 +2258 # . . push args +2259 68/push "\"a\\\"b\"/yz"/imm32 +2260 68/push _test-input-stream/imm32 2261 # . . call -2262 e8/call check-ints-equal/disp32 +2262 e8/call write/disp32 2263 # . . discard args -2264 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2265 # skip-string(_test-input-stream) +2264 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2265 # precondition: line->read == 0 2266 # . . push args -2267 68/push _test-input-stream/imm32 -2268 # . . call -2269 e8/call skip-string/disp32 -2270 # . . discard args -2271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2272 # check-ints-equal(line->read, 6, msg) -2273 # . . push args -2274 68/push "F - test-skip-string-ignores-escapes"/imm32 -2275 68/push 6/imm32 -2276 b8/copy-to-EAX _test-input-stream/imm32 -2277 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2267 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 +2268 68/push 0/imm32 +2269 b8/copy-to-EAX _test-input-stream/imm32 +2270 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2271 # . . call +2272 e8/call check-ints-equal/disp32 +2273 # . . discard args +2274 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2275 # skip-string(_test-input-stream) +2276 # . . push args +2277 68/push _test-input-stream/imm32 2278 # . . call -2279 e8/call check-ints-equal/disp32 +2279 e8/call skip-string/disp32 2280 # . . discard args -2281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2282 # . epilog -2283 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2284 5d/pop-to-EBP -2285 c3/return -2286 -2287 test-skip-string-works-from-mid-stream: -2288 # . prolog -2289 55/push-EBP -2290 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2291 # setup -2292 # . clear-stream(_test-input-stream) -2293 # . . push args -2294 68/push _test-input-stream/imm32 -2295 # . . call -2296 e8/call clear-stream/disp32 -2297 # . . discard args -2298 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2299 # . write(_test-input-stream, "0 \"a\\\"b\"/yz") -2300 # . indices: 01 2 34 56 -2301 # . . push args -2302 68/push "0 \"a\\\"b\"/yz"/imm32 -2303 68/push _test-input-stream/imm32 -2304 # . . call -2305 e8/call write/disp32 -2306 # . . discard args -2307 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2308 # precondition: line->read == 2 -2309 c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 2/imm32 # copy to *(EAX+4) -2310 # skip-string(_test-input-stream) +2281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2282 # check-ints-equal(line->read, 6, msg) +2283 # . . push args +2284 68/push "F - test-skip-string-ignores-escapes"/imm32 +2285 68/push 6/imm32 +2286 b8/copy-to-EAX _test-input-stream/imm32 +2287 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2288 # . . call +2289 e8/call check-ints-equal/disp32 +2290 # . . discard args +2291 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2292 # . epilog +2293 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2294 5d/pop-to-EBP +2295 c3/return +2296 +2297 test-skip-string-works-from-mid-stream: +2298 # . prolog +2299 55/push-EBP +2300 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2301 # setup +2302 # . clear-stream(_test-input-stream) +2303 # . . push args +2304 68/push _test-input-stream/imm32 +2305 # . . call +2306 e8/call clear-stream/disp32 +2307 # . . discard args +2308 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2309 # . write(_test-input-stream, "0 \"a\\\"b\"/yz") +2310 # . indices: 01 2 34 56 2311 # . . push args -2312 68/push _test-input-stream/imm32 -2313 # . . call -2314 e8/call skip-string/disp32 -2315 # . . discard args -2316 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2317 # check-ints-equal(line->read, 8, msg) -2318 # . . push args -2319 68/push "F - test-skip-string-works-from-mid-stream"/imm32 -2320 68/push 8/imm32 -2321 b8/copy-to-EAX _test-input-stream/imm32 -2322 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2312 68/push "0 \"a\\\"b\"/yz"/imm32 +2313 68/push _test-input-stream/imm32 +2314 # . . call +2315 e8/call write/disp32 +2316 # . . discard args +2317 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2318 # precondition: line->read == 2 +2319 c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 2/imm32 # copy to *(EAX+4) +2320 # skip-string(_test-input-stream) +2321 # . . push args +2322 68/push _test-input-stream/imm32 2323 # . . call -2324 e8/call check-ints-equal/disp32 +2324 e8/call skip-string/disp32 2325 # . . discard args -2326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2327 # . epilog -2328 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2329 5d/pop-to-EBP -2330 c3/return -2331 -2332 skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/EAX -2333 # . prolog -2334 55/push-EBP -2335 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2336 # . save registers -2337 51/push-ECX -2338 52/push-EDX -2339 53/push-EBX -2340 # ECX = curr -2341 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -2342 # EDX = end -2343 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -2344 # EAX = 0 -2345 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2346 # skip initial dquote -2347 41/increment-ECX -2348 $skip-string-in-slice:loop: -2349 # if (curr >= end) return curr -2350 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -2351 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 -2352 # AL = *curr -2353 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -2354 $skip-string-in-slice:dquote: -2355 # if (EAX == '"') break -2356 3d/compare-EAX-and 0x22/imm32/double-quote -2357 74/jump-if-equal $skip-string-in-slice:break/disp8 -2358 $skip-string-in-slice:check-for-escape: -2359 # if (EAX == '\') escape next char -2360 3d/compare-EAX-and 0x5c/imm32/backslash -2361 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 -2362 $skip-string-in-slice:escape: -2363 41/increment-ECX -2364 $skip-string-in-slice:continue: -2365 # ++curr -2366 41/increment-ECX -2367 eb/jump $skip-string-in-slice:loop/disp8 -2368 $skip-string-in-slice:break: -2369 # skip final dquote -2370 41/increment-ECX -2371 $skip-string-in-slice:return-curr: -2372 # return curr -2373 89/copy 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to EAX -2374 $skip-string-in-slice:end: -2375 # . restore registers -2376 5b/pop-to-EBX -2377 5a/pop-to-EDX -2378 59/pop-to-ECX -2379 # . epilog -2380 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2381 5d/pop-to-EBP -2382 c3/return -2383 -2384 test-skip-string-in-slice: -2385 # . prolog -2386 55/push-EBP -2387 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2388 # setup: (EAX..ECX) = "\"abc\" def" -2389 b8/copy-to-EAX "\"abc\" def"/imm32 -2390 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2391 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -2392 05/add-to-EAX 4/imm32 -2393 # EAX = skip-string-in-slice(EAX, ECX) -2394 # . . push args -2395 51/push-ECX -2396 50/push-EAX -2397 # . . call -2398 e8/call skip-string-in-slice/disp32 -2399 # . . discard args -2400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2401 # check-ints-equal(ECX-EAX, 4, msg) # number of chars remaining after the string literal -2402 # . . push args -2403 68/push "F - test-skip-string-in-slice"/imm32 -2404 68/push 4/imm32 -2405 # . . push ECX-EAX -2406 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2407 51/push-ECX -2408 # . . call -2409 e8/call check-ints-equal/disp32 -2410 # . . discard args -2411 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2412 # . epilog -2413 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2414 5d/pop-to-EBP -2415 c3/return -2416 -2417 test-skip-string-in-slice-ignores-spaces: -2418 # . prolog -2419 55/push-EBP -2420 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2421 # setup: (EAX..ECX) = "\"a b\"/yz" -2422 b8/copy-to-EAX "\"a b\"/yz"/imm32 -2423 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2424 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -2425 05/add-to-EAX 4/imm32 -2426 # EAX = skip-string-in-slice(EAX, ECX) -2427 # . . push args -2428 51/push-ECX -2429 50/push-EAX -2430 # . . call -2431 e8/call skip-string-in-slice/disp32 -2432 # . . discard args -2433 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2434 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal -2435 # . . push args -2436 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 -2437 68/push 3/imm32 -2438 # . . push ECX-EAX -2439 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2440 51/push-ECX -2441 # . . call -2442 e8/call check-ints-equal/disp32 -2443 # . . discard args -2444 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2445 # . epilog -2446 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2447 5d/pop-to-EBP -2448 c3/return -2449 -2450 test-skip-string-in-slice-ignores-escapes: -2451 # . prolog -2452 55/push-EBP -2453 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2454 # setup: (EAX..ECX) = "\"a\\\"b\"/yz" -2455 b8/copy-to-EAX "\"a\\\"b\"/yz"/imm32 -2456 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2457 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -2458 05/add-to-EAX 4/imm32 -2459 # EAX = skip-string-in-slice(EAX, ECX) -2460 # . . push args -2461 51/push-ECX -2462 50/push-EAX -2463 # . . call -2464 e8/call skip-string-in-slice/disp32 -2465 # . . discard args -2466 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2467 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal -2468 # . . push args -2469 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 -2470 68/push 3/imm32 -2471 # . . push ECX-EAX -2472 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2473 51/push-ECX -2474 # . . call -2475 e8/call check-ints-equal/disp32 -2476 # . . discard args -2477 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2478 # . epilog -2479 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2480 5d/pop-to-EBP -2481 c3/return -2482 -2483 test-skip-string-in-slice-stops-at-end: -2484 # . prolog -2485 55/push-EBP -2486 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2487 # setup: (EAX..ECX) = "\"abc" # unbalanced dquote -2488 b8/copy-to-EAX "\"abc"/imm32 -2489 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2490 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -2491 05/add-to-EAX 4/imm32 -2492 # EAX = skip-string-in-slice(EAX, ECX) -2493 # . . push args -2494 51/push-ECX -2495 50/push-EAX -2496 # . . call -2497 e8/call skip-string-in-slice/disp32 -2498 # . . discard args -2499 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2500 # check-ints-equal(ECX-EAX, 0, msg) # skipped to end of slice -2501 # . . push args -2502 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 -2503 68/push 0/imm32 -2504 # . . push ECX-EAX -2505 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2506 51/push-ECX -2507 # . . call -2508 e8/call check-ints-equal/disp32 -2509 # . . discard args -2510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2511 # . epilog -2512 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2513 5d/pop-to-EBP -2514 c3/return -2515 -2516 string-length-at-start-of-slice: # curr : (address byte), end : (address byte) -> length/EAX -2517 # . prolog -2518 55/push-EBP -2519 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2520 # . save registers -2521 51/push-ECX -2522 52/push-EDX -2523 53/push-EBX -2524 # ECX = curr -2525 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -2526 # EDX = end -2527 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -2528 # length/EAX = 0 -2529 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2530 # EBX = 0 -2531 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -2532 # skip initial dquote -2533 41/increment-ECX -2534 $string-length-at-start-of-slice:loop: -2535 # if (curr >= end) return length -2536 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -2537 73/jump-if-greater-unsigned-or-equal $string-length-at-start-of-slice:end/disp8 -2538 # BL = *curr -2539 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL -2540 $string-length-at-start-of-slice:dquote: -2541 # if (EBX == '"') break -2542 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x22/imm32/dquote # compare EBX -2543 74/jump-if-equal $string-length-at-start-of-slice:end/disp8 -2544 $string-length-at-start-of-slice:check-for-escape: -2545 # if (EBX == '\') escape next char -2546 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x5c/imm32/backslash # compare EBX -2547 75/jump-if-not-equal $string-length-at-start-of-slice:continue/disp8 -2548 $string-length-at-start-of-slice:escape: -2549 # increment curr but not result -2550 41/increment-ECX -2551 $string-length-at-start-of-slice:continue: -2552 # ++result -2553 40/increment-EAX -2554 # ++curr -2555 41/increment-ECX -2556 eb/jump $string-length-at-start-of-slice:loop/disp8 -2557 $string-length-at-start-of-slice:end: -2558 # . restore registers -2559 5b/pop-to-EBX -2560 5a/pop-to-EDX -2561 59/pop-to-ECX -2562 # . epilog -2563 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2564 5d/pop-to-EBP -2565 c3/return -2566 -2567 test-string-length-at-start-of-slice: -2568 # . prolog -2569 55/push-EBP -2570 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2571 # setup: (EAX..ECX) = "\"abc\" def" -2572 b8/copy-to-EAX "\"abc\" def"/imm32 -2573 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2574 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -2575 05/add-to-EAX 4/imm32 -2576 # EAX = string-length-at-start-of-slice(EAX, ECX) -2577 # . . push args -2578 51/push-ECX -2579 50/push-EAX -2580 # . . call -2581 e8/call string-length-at-start-of-slice/disp32 -2582 # . . discard args -2583 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2584 # check-ints-equal(EAX, 3, msg) -2585 # . . push args -2586 68/push "F - test-string-length-at-start-of-slice"/imm32 -2587 68/push 3/imm32 -2588 50/push-EAX -2589 # . . call -2590 e8/call check-ints-equal/disp32 -2591 # . . discard args -2592 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2593 # . epilog -2594 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2595 5d/pop-to-EBP -2596 c3/return -2597 -2598 test-string-length-at-start-of-slice-escaped: -2599 # . prolog -2600 55/push-EBP -2601 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2602 # setup: (EAX..ECX) = "\"ab\\c\" def" -2603 b8/copy-to-EAX "\"ab\\c\" def"/imm32 -2604 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2605 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -2606 05/add-to-EAX 4/imm32 -2607 # EAX = string-length-at-start-of-slice(EAX, ECX) -2608 # . . push args -2609 51/push-ECX -2610 50/push-EAX -2611 # . . call -2612 e8/call string-length-at-start-of-slice/disp32 -2613 # . . discard args -2614 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2615 # check-ints-equal(EAX, 3, msg) -2616 # . . push args -2617 68/push "F - test-string-length-at-start-of-slice-escaped"/imm32 -2618 68/push 3/imm32 -2619 50/push-EAX -2620 # . . call -2621 e8/call check-ints-equal/disp32 -2622 # . . discard args -2623 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2624 # . epilog -2625 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2626 5d/pop-to-EBP -2627 c3/return -2628 -2629 == data -2630 -2631 Segment-size: -2632 0x1000/imm32/4KB -2633 -2634 Next-string-literal: # tracks the next auto-generated variable name -2635 1/imm32 -2636 -2637 Heap: -2638 # curr -2639 0/imm32 -2640 # limit -2641 0/imm32 -2642 -2643 # length-prefixed string containing just a single space -2644 Space: -2645 # size -2646 1/imm32 -2647 # data -2648 20/space -2649 -2650 # length-prefixed string containing just a single slash -2651 Slash: -2652 # size -2653 1/imm32 -2654 # data -2655 2f/slash -2656 -2657 _test-slice-abc: -2658 22/dquote 61/a 62/b 63/c 22/dquote # "abc" -2659 _test-slice-abc-end: -2660 2f/slash 64/d -2661 _test-slice-abc-metadata-end: -2662 -2663 _test-slice-empty-string-literal: -2664 22/dquote 22/dquote # "" -2665 _test-slice-empty-string-literal-end: -2666 -2667 _test-slice-a-space-b: -2668 22/dquote 61/a 20/space 62/b 22/dquote # "a b" -2669 _test-slice-a-space-b-end: -2670 -2671 _test-slice-a-dquote-b: -2672 22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote # "a\"b" -2673 _test-slice-a-dquote-b-end: -2674 -2675 # abc/def/ghi -2676 _test-slice-word: +2326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2327 # check-ints-equal(line->read, 8, msg) +2328 # . . push args +2329 68/push "F - test-skip-string-works-from-mid-stream"/imm32 +2330 68/push 8/imm32 +2331 b8/copy-to-EAX _test-input-stream/imm32 +2332 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +2333 # . . call +2334 e8/call check-ints-equal/disp32 +2335 # . . discard args +2336 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2337 # . epilog +2338 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2339 5d/pop-to-EBP +2340 c3/return +2341 +2342 skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/EAX +2343 # . prolog +2344 55/push-EBP +2345 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2346 # . save registers +2347 51/push-ECX +2348 52/push-EDX +2349 53/push-EBX +2350 # ECX = curr +2351 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +2352 # EDX = end +2353 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX +2354 # EAX = 0 +2355 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2356 # skip initial dquote +2357 41/increment-ECX +2358 $skip-string-in-slice:loop: +2359 # if (curr >= end) return curr +2360 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX +2361 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 +2362 # AL = *curr +2363 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +2364 $skip-string-in-slice:dquote: +2365 # if (EAX == '"') break +2366 3d/compare-EAX-and 0x22/imm32/double-quote +2367 74/jump-if-equal $skip-string-in-slice:break/disp8 +2368 $skip-string-in-slice:check-for-escape: +2369 # if (EAX == '\') escape next char +2370 3d/compare-EAX-and 0x5c/imm32/backslash +2371 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 +2372 $skip-string-in-slice:escape: +2373 41/increment-ECX +2374 $skip-string-in-slice:continue: +2375 # ++curr +2376 41/increment-ECX +2377 eb/jump $skip-string-in-slice:loop/disp8 +2378 $skip-string-in-slice:break: +2379 # skip final dquote +2380 41/increment-ECX +2381 $skip-string-in-slice:return-curr: +2382 # return curr +2383 89/copy 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to EAX +2384 $skip-string-in-slice:end: +2385 # . restore registers +2386 5b/pop-to-EBX +2387 5a/pop-to-EDX +2388 59/pop-to-ECX +2389 # . epilog +2390 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2391 5d/pop-to-EBP +2392 c3/return +2393 +2394 test-skip-string-in-slice: +2395 # . prolog +2396 55/push-EBP +2397 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2398 # setup: (EAX..ECX) = "\"abc\" def" +2399 b8/copy-to-EAX "\"abc\" def"/imm32 +2400 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2401 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +2402 05/add-to-EAX 4/imm32 +2403 # EAX = skip-string-in-slice(EAX, ECX) +2404 # . . push args +2405 51/push-ECX +2406 50/push-EAX +2407 # . . call +2408 e8/call skip-string-in-slice/disp32 +2409 # . . discard args +2410 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2411 # check-ints-equal(ECX-EAX, 4, msg) # number of chars remaining after the string literal +2412 # . . push args +2413 68/push "F - test-skip-string-in-slice"/imm32 +2414 68/push 4/imm32 +2415 # . . push ECX-EAX +2416 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX +2417 51/push-ECX +2418 # . . call +2419 e8/call check-ints-equal/disp32 +2420 # . . discard args +2421 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2422 # . epilog +2423 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2424 5d/pop-to-EBP +2425 c3/return +2426 +2427 test-skip-string-in-slice-ignores-spaces: +2428 # . prolog +2429 55/push-EBP +2430 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2431 # setup: (EAX..ECX) = "\"a b\"/yz" +2432 b8/copy-to-EAX "\"a b\"/yz"/imm32 +2433 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2434 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +2435 05/add-to-EAX 4/imm32 +2436 # EAX = skip-string-in-slice(EAX, ECX) +2437 # . . push args +2438 51/push-ECX +2439 50/push-EAX +2440 # . . call +2441 e8/call skip-string-in-slice/disp32 +2442 # . . discard args +2443 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2444 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal +2445 # . . push args +2446 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 +2447 68/push 3/imm32 +2448 # . . push ECX-EAX +2449 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX +2450 51/push-ECX +2451 # . . call +2452 e8/call check-ints-equal/disp32 +2453 # . . discard args +2454 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2455 # . epilog +2456 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2457 5d/pop-to-EBP +2458 c3/return +2459 +2460 test-skip-string-in-slice-ignores-escapes: +2461 # . prolog +2462 55/push-EBP +2463 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2464 # setup: (EAX..ECX) = "\"a\\\"b\"/yz" +2465 b8/copy-to-EAX "\"a\\\"b\"/yz"/imm32 +2466 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2467 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +2468 05/add-to-EAX 4/imm32 +2469 # EAX = skip-string-in-slice(EAX, ECX) +2470 # . . push args +2471 51/push-ECX +2472 50/push-EAX +2473 # . . call +2474 e8/call skip-string-in-slice/disp32 +2475 # . . discard args +2476 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2477 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal +2478 # . . push args +2479 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 +2480 68/push 3/imm32 +2481 # . . push ECX-EAX +2482 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX +2483 51/push-ECX +2484 # . . call +2485 e8/call check-ints-equal/disp32 +2486 # . . discard args +2487 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2488 # . epilog +2489 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2490 5d/pop-to-EBP +2491 c3/return +2492 +2493 test-skip-string-in-slice-stops-at-end: +2494 # . prolog +2495 55/push-EBP +2496 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2497 # setup: (EAX..ECX) = "\"abc" # unbalanced dquote +2498 b8/copy-to-EAX "\"abc"/imm32 +2499 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2500 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +2501 05/add-to-EAX 4/imm32 +2502 # EAX = skip-string-in-slice(EAX, ECX) +2503 # . . push args +2504 51/push-ECX +2505 50/push-EAX +2506 # . . call +2507 e8/call skip-string-in-slice/disp32 +2508 # . . discard args +2509 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2510 # check-ints-equal(ECX-EAX, 0, msg) # skipped to end of slice +2511 # . . push args +2512 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 +2513 68/push 0/imm32 +2514 # . . push ECX-EAX +2515 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX +2516 51/push-ECX +2517 # . . call +2518 e8/call check-ints-equal/disp32 +2519 # . . discard args +2520 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2521 # . epilog +2522 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2523 5d/pop-to-EBP +2524 c3/return +2525 +2526 string-length-at-start-of-slice: # curr : (address byte), end : (address byte) -> length/EAX +2527 # . prolog +2528 55/push-EBP +2529 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2530 # . save registers +2531 51/push-ECX +2532 52/push-EDX +2533 53/push-EBX +2534 # ECX = curr +2535 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +2536 # EDX = end +2537 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX +2538 # length/EAX = 0 +2539 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2540 # EBX = 0 +2541 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX +2542 # skip initial dquote +2543 41/increment-ECX +2544 $string-length-at-start-of-slice:loop: +2545 # if (curr >= end) return length +2546 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX +2547 73/jump-if-greater-unsigned-or-equal $string-length-at-start-of-slice:end/disp8 +2548 # BL = *curr +2549 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL +2550 $string-length-at-start-of-slice:dquote: +2551 # if (EBX == '"') break +2552 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x22/imm32/dquote # compare EBX +2553 74/jump-if-equal $string-length-at-start-of-slice:end/disp8 +2554 $string-length-at-start-of-slice:check-for-escape: +2555 # if (EBX == '\') escape next char +2556 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x5c/imm32/backslash # compare EBX +2557 75/jump-if-not-equal $string-length-at-start-of-slice:continue/disp8 +2558 $string-length-at-start-of-slice:escape: +2559 # increment curr but not result +2560 41/increment-ECX +2561 $string-length-at-start-of-slice:continue: +2562 # ++result +2563 40/increment-EAX +2564 # ++curr +2565 41/increment-ECX +2566 eb/jump $string-length-at-start-of-slice:loop/disp8 +2567 $string-length-at-start-of-slice:end: +2568 # . restore registers +2569 5b/pop-to-EBX +2570 5a/pop-to-EDX +2571 59/pop-to-ECX +2572 # . epilog +2573 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2574 5d/pop-to-EBP +2575 c3/return +2576 +2577 test-string-length-at-start-of-slice: +2578 # . prolog +2579 55/push-EBP +2580 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2581 # setup: (EAX..ECX) = "\"abc\" def" +2582 b8/copy-to-EAX "\"abc\" def"/imm32 +2583 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2584 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +2585 05/add-to-EAX 4/imm32 +2586 # EAX = string-length-at-start-of-slice(EAX, ECX) +2587 # . . push args +2588 51/push-ECX +2589 50/push-EAX +2590 # . . call +2591 e8/call string-length-at-start-of-slice/disp32 +2592 # . . discard args +2593 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2594 # check-ints-equal(EAX, 3, msg) +2595 # . . push args +2596 68/push "F - test-string-length-at-start-of-slice"/imm32 +2597 68/push 3/imm32 +2598 50/push-EAX +2599 # . . call +2600 e8/call check-ints-equal/disp32 +2601 # . . discard args +2602 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2603 # . epilog +2604 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2605 5d/pop-to-EBP +2606 c3/return +2607 +2608 test-string-length-at-start-of-slice-escaped: +2609 # . prolog +2610 55/push-EBP +2611 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2612 # setup: (EAX..ECX) = "\"ab\\c\" def" +2613 b8/copy-to-EAX "\"ab\\c\" def"/imm32 +2614 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2615 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX +2616 05/add-to-EAX 4/imm32 +2617 # EAX = string-length-at-start-of-slice(EAX, ECX) +2618 # . . push args +2619 51/push-ECX +2620 50/push-EAX +2621 # . . call +2622 e8/call string-length-at-start-of-slice/disp32 +2623 # . . discard args +2624 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2625 # check-ints-equal(EAX, 3, msg) +2626 # . . push args +2627 68/push "F - test-string-length-at-start-of-slice-escaped"/imm32 +2628 68/push 3/imm32 +2629 50/push-EAX +2630 # . . call +2631 e8/call check-ints-equal/disp32 +2632 # . . discard args +2633 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2634 # . epilog +2635 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2636 5d/pop-to-EBP +2637 c3/return +2638 +2639 == data +2640 +2641 Segment-size: +2642 0x1000/imm32/4KB +2643 +2644 Next-string-literal: # tracks the next auto-generated variable name +2645 1/imm32 +2646 +2647 # length-prefixed string containing just a single space +2648 Space: +2649 # size +2650 1/imm32 +2651 # data +2652 20/space +2653 +2654 # length-prefixed string containing just a single slash +2655 Slash: +2656 # size +2657 1/imm32 +2658 # data +2659 2f/slash +2660 +2661 _test-slice-abc: +2662 22/dquote 61/a 62/b 63/c 22/dquote # "abc" +2663 2f/slash 64/d +2664 _test-slice-abc-limit: +2665 +2666 _test-slice-a-space-b: +2667 22/dquote 61/a 20/space 62/b 22/dquote # "a b" +2668 _test-slice-a-space-b-limit: +2669 +2670 _test-slice-a-dquote-b: +2671 22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote # "a\"b" +2672 _test-slice-a-dquote-b-limit: +2673 +2674 # "abc/def"/ghi +2675 _test-slice-literal-string: +2676 22/dquote 2677 61/a 62/b 63/c # abc -2678 _test-slice-word-datum-end: -2679 2f/slash 64/d 65/e 66/f # /def -2680 _test-slice-word-end: -2681 2f/slash 67/g 68/h 69/i # /ghi -2682 _test-slice-word-end2: -2683 -2684 # "abc/def"/ghi -2685 _test-slice-literal-string: -2686 22/dquote -2687 61/a 62/b 63/c # abc -2688 2f/slash 64/d 65/e 66/f # /def -2689 22/dquote -2690 _test-slice-literal-string-end: -2691 2f/slash 67/g 68/h 69/i # /ghi -2692 _test-slice-literal-string-with-metadata-end: -2693 -2694 # . . vim:nowrap:textwidth=0 +2678 2f/slash 64/d 65/e 66/f # /def +2679 22/dquote +2680 2f/slash 67/g 68/h 69/i # /ghi +2681 _test-slice-literal-string-with-limit: +2682 +2683 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/factorial.subx.html b/html/subx/apps/factorial.subx.html index 27859afe..ee50c85a 100644 --- a/html/subx/apps/factorial.subx.html +++ b/html/subx/apps/factorial.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/factorial.subx - - + + @@ -14,17 +14,16 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -41,7 +40,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -81,97 +80,102 @@ if ('onhashchange' in window) { 19 # . 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 20 21 Entry: # run tests if necessary, compute `factorial(5)` if not - 22 - 23 #? # for debugging: run a single test; don't bother setting status code - 24 #? e8/call test-get-num-reads-single-digit/disp32 - 25 #? eb/jump $main:end/disp8 - 26 - 27 # . prolog - 28 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 29 # - if argc > 1 and argv[1] == "test", then return run_tests() - 30 # . argc > 1 - 31 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 32 7e/jump-if-lesser-or-equal $run-main/disp8 - 33 # . argv[1] == "test" - 34 # . . push args - 35 68/push "test"/imm32 - 36 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 37 # . . call - 38 e8/call kernel-string-equal?/disp32 - 39 # . . discard args - 40 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 41 # . check result - 42 3d/compare-EAX-and 1/imm32 - 43 75/jump-if-not-equal $run-main/disp8 - 44 # . run-tests() - 45 e8/call run-tests/disp32 - 46 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Num-test-failures/disp32 # copy *Num-test-failures to EAX - 47 eb/jump $main:end/disp8 # where EAX will get copied to EBX - 48 $run-main: - 49 # - otherwise return factorial(5) - 50 # . . push args - 51 68/push 5/imm32 - 52 # . . call - 53 e8/call factorial/disp32 - 54 # . . discard args - 55 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 56 $main:end: - 57 # syscall(exit, EAX) - 58 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - 59 b8/copy-to-EAX 1/imm32/exit - 60 cd/syscall 0x80/imm8 - 61 - 62 factorial: # n : int -> int/EAX - 63 # . prolog - 64 55/push-EBP - 65 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 66 53/push-EBX - 67 # EAX = 1 (base case) - 68 b8/copy-to-EAX 1/imm32 - 69 # if (n <= 1) return - 70 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 1/imm32 # compare *(EBP+8) - 71 7e/jump-if-<= $factorial:end/disp8 - 72 # EBX = n-1 - 73 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX - 74 81 5/subop/subtract 3/mod/direct 3/rm32/EBX . . . . . 1/imm32 # subtract from EBX - 75 # EAX = factorial(n-1) - 76 # . . push args - 77 53/push-EBX - 78 # . . call - 79 e8/call factorial/disp32 - 80 # . . discard args - 81 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 82 # return n * factorial(n-1) - 83 f7 4/subop/multiply 1/mod/*+disp8 5/rm32/EBP . . 8/disp8 . # multiply *(EBP+8) into EAX - 84 # TODO: check for overflow - 85 $factorial:end: - 86 # . epilog - 87 5b/pop-to-EBX - 88 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 89 5d/pop-to-EBP - 90 c3/return - 91 - 92 test-factorial: - 93 # factorial(5) - 94 # . . push args - 95 68/push 5/imm32 - 96 # . . call - 97 e8/call factorial/disp32 - 98 # . . discard args - 99 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -100 # check-ints-equal(EAX, 120, msg) -101 # . . push args -102 68/push "F - test-factorial"/imm32 -103 68/push 0x78/imm32/expected-120 -104 50/push-EAX -105 # . . call -106 e8/call check-ints-equal/disp32 -107 # . . discard args -108 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -109 # end -110 c3/return -111 -112 # . . vim:nowrap:textwidth=0 + 22 # initialize heap + 23 # . Heap = new-segment(64KB) + 24 # . . push args + 25 68/push Heap/imm32 + 26 68/push 0x10000/imm32/64KB + 27 # . . call + 28 e8/call new-segment/disp32 + 29 # . . discard args + 30 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 31 + 32 # . prolog + 33 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 34 # - if argc > 1 and argv[1] == "test", then return run_tests() + 35 # . argc > 1 + 36 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP + 37 7e/jump-if-lesser-or-equal $run-main/disp8 + 38 # . argv[1] == "test" + 39 # . . push args + 40 68/push "test"/imm32 + 41 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 42 # . . call + 43 e8/call kernel-string-equal?/disp32 + 44 # . . discard args + 45 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 46 # . check result + 47 3d/compare-EAX-and 1/imm32 + 48 75/jump-if-not-equal $run-main/disp8 + 49 # . run-tests() + 50 e8/call run-tests/disp32 + 51 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Num-test-failures/disp32 # copy *Num-test-failures to EAX + 52 eb/jump $main:end/disp8 # where EAX will get copied to EBX + 53 $run-main: + 54 # - otherwise return factorial(5) + 55 # . . push args + 56 68/push 5/imm32 + 57 # . . call + 58 e8/call factorial/disp32 + 59 # . . discard args + 60 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 61 $main:end: + 62 # syscall(exit, EAX) + 63 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + 64 b8/copy-to-EAX 1/imm32/exit + 65 cd/syscall 0x80/imm8 + 66 + 67 factorial: # n : int -> int/EAX + 68 # . prolog + 69 55/push-EBP + 70 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 71 53/push-EBX + 72 # EAX = 1 (base case) + 73 b8/copy-to-EAX 1/imm32 + 74 # if (n <= 1) return + 75 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 1/imm32 # compare *(EBP+8) + 76 7e/jump-if-<= $factorial:end/disp8 + 77 # EBX = n-1 + 78 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX + 79 81 5/subop/subtract 3/mod/direct 3/rm32/EBX . . . . . 1/imm32 # subtract from EBX + 80 # EAX = factorial(n-1) + 81 # . . push args + 82 53/push-EBX + 83 # . . call + 84 e8/call factorial/disp32 + 85 # . . discard args + 86 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 87 # return n * factorial(n-1) + 88 f7 4/subop/multiply 1/mod/*+disp8 5/rm32/EBP . . 8/disp8 . # multiply *(EBP+8) into EAX + 89 # TODO: check for overflow + 90 $factorial:end: + 91 # . epilog + 92 5b/pop-to-EBX + 93 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 94 5d/pop-to-EBP + 95 c3/return + 96 + 97 test-factorial: + 98 # factorial(5) + 99 # . . push args +100 68/push 5/imm32 +101 # . . call +102 e8/call factorial/disp32 +103 # . . discard args +104 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +105 # check-ints-equal(EAX, 120, msg) +106 # . . push args +107 68/push "F - test-factorial"/imm32 +108 68/push 0x78/imm32/expected-120 +109 50/push-EAX +110 # . . call +111 e8/call check-ints-equal/disp32 +112 # . . discard args +113 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +114 # end +115 c3/return +116 +117 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/handle.subx.html b/html/subx/apps/handle.subx.html index 6b6adc69..4f241691 100644 --- a/html/subx/apps/handle.subx.html +++ b/html/subx/apps/handle.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/handle.subx - - + + @@ -14,17 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxTest { color: #5f8700; } -.Constant { color: #008787; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .CommentedCode { color: #8a8a8a; } +.subxTest { color: #5f8700; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } .subxH1Comment { color: #005faf; text-decoration: underline; } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -41,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ diff --git a/html/subx/apps/hex.subx.html b/html/subx/apps/hex.subx.html index 53376ea7..431c1c2c 100644 --- a/html/subx/apps/hex.subx.html +++ b/html/subx/apps/hex.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/hex.subx - - + + @@ -14,18 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxMinorFunction { color: #875f5f; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -42,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -81,1497 +80,1501 @@ if ('onhashchange' in window) { 18 # . 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 19 20 Entry: # run tests if necessary, convert stdin if not - 21 - 22 #? # for debugging: run a single test - 23 #? e8/call test-convert-next-octet-aborts-on-single-hex-byte/disp32 - 24 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 25 #? eb/jump $main:end/disp8 - 26 - 27 # . prolog - 28 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 29 # - if argc > 1 and argv[1] == "test", then return run_tests() - 30 # . argc > 1 - 31 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 32 7e/jump-if-lesser-or-equal $run-main/disp8 - 33 # . argv[1] == "test" - 34 # . . push args - 35 68/push "test"/imm32 - 36 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 37 # . . call - 38 e8/call kernel-string-equal?/disp32 - 39 # . . discard args - 40 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 41 # . check result - 42 3d/compare-EAX-and 1/imm32 - 43 75/jump-if-not-equal $run-main/disp8 - 44 # . run-tests() - 45 e8/call run-tests/disp32 - 46 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 47 eb/jump $main:end/disp8 - 48 $run-main: - 49 # - otherwise convert stdin - 50 # var ed/EAX : exit-descriptor - 51 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 52 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - 53 # configure ed to really exit() - 54 # . ed->target = 0 - 55 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 56 # return convert(Stdin, 1/stdout, 2/stderr, ed) - 57 # . . push args - 58 50/push-EAX/ed - 59 68/push Stderr/imm32 - 60 68/push Stdout/imm32 - 61 68/push Stdin/imm32 - 62 # . . call - 63 e8/call convert/disp32 - 64 # . . discard args - 65 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 66 # . syscall(exit, 0) - 67 bb/copy-to-EBX 0/imm32 - 68 $main:end: - 69 b8/copy-to-EAX 1/imm32/exit - 70 cd/syscall 0x80/imm8 - 71 - 72 # the main entry point - 73 convert: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void> - 74 # pseudocode: - 75 # while true - 76 # EAX = convert-next-octet(in, err, ed) - 77 # if (EAX == Eof) break - 78 # write-byte-buffered(out, AL) - 79 # flush(out) - 80 # - 81 # . prolog - 82 55/push-EBP - 83 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 84 # . save registers - 85 50/push-EAX - 86 $convert:loop: - 87 # EAX = convert-next-octet(in, err, ed) - 88 # . . push args - 89 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - 90 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 91 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 92 # . . call - 93 e8/call convert-next-octet/disp32 - 94 # . . discard first 2 args - 95 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 96 # if (EAX == Eof) break - 97 3d/compare-EAX-and 0xffffffff/imm32/Eof - 98 74/jump-if-equal $convert:loop-end/disp8 - 99 # write-byte-buffered(out, AL) - 100 # . . push args - 101 50/push-EAX - 102 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 103 # . . call - 104 e8/call write-byte-buffered/disp32 - 105 # . . discard args - 106 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 107 # loop - 108 eb/jump $convert:loop/disp8 - 109 $convert:loop-end: - 110 # flush(out) - 111 # . . push args - 112 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 113 # . . call - 114 e8/call flush/disp32 - 115 # . . discard args - 116 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 117 $convert:end: - 118 # . restore registers - 119 58/pop-to-EAX - 120 # . epilog - 121 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 122 5d/pop-to-EBP - 123 c3/return - 124 - 125 # read bytes from 'in' until a sequence of two lowercase hex (0-9, a-f) bytes - 126 # skip spaces and newlines - 127 # on '#' skip bytes until newline - 128 # raise an error and abort on all other unexpected bytes - 129 # return in EAX an _octet_ containing the binary value of the two hex characters - 130 # return Eof on reaching end of file - 131 convert-next-octet: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX - 132 # pseudocode: - 133 # EAX = scan-next-byte(in, err, ed) - 134 # if (EAX == Eof) return - 135 # ECX = from-hex-char(EAX) - 136 # EAX = scan-next-byte(in, err, ed) - 137 # if (EAX == Eof) error("partial byte found.") - 138 # EAX = from-hex-char(EAX) - 139 # EAX = (ECX << 4) | EAX - 140 # return - 141 # - 142 # . prolog - 143 55/push-EBP - 144 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 145 # . save registers - 146 51/push-ECX - 147 # EAX = scan-next-byte(in, err, ed) - 148 # . . push args - 149 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 150 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 151 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 152 # . . call - 153 e8/call scan-next-byte/disp32 - 154 # . . discard args - 155 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 156 # if (EAX == Eof) return - 157 3d/compare-EAX-and 0xffffffff/imm32/Eof - 158 74/jump-if-equal $convert-next-octet:end/disp8 - 159 # EAX = from-hex-char(EAX) - 160 e8/call from-hex-char/disp32 - 161 # ECX = EAX - 162 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - 163 # EAX = scan-next-byte(in, err, ed) - 164 # . . push args - 165 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 166 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 167 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 168 # . . call - 169 e8/call scan-next-byte/disp32 - 170 # . . discard args - 171 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 172 # if (EAX == Eof) error(ed, err, "partial byte found.") - 173 3d/compare-EAX-and 0xffffffff/imm32/Eof - 174 75/jump-if-not-equal $convert-next-octet:convert/disp8 - 175 # . error-byte(ed, err, msg, '.') # reusing error-byte to avoid creating _yet_ another helper - 176 # . . push args - 177 68/push 0x2e/imm32/period/dummy - 178 68/push "convert-next-octet: partial byte found"/imm32 - 179 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 180 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 181 # . . call - 182 e8/call error-byte/disp32 # never returns - 183 $convert-next-octet:convert: - 184 # EAX = from-hex-char(EAX) - 185 e8/call from-hex-char/disp32 - 186 # EAX = (ECX << 4) | EAX - 187 # . ECX <<= 4 - 188 c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX . . . . . 4/imm8 # shift ECX left by 4 bits - 189 # . EAX |= ECX - 190 09/or 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # EAX = bitwise OR with ECX - 191 $convert-next-octet:end: - 192 # . restore registers - 193 59/pop-to-ECX - 194 # . epilog - 195 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 196 5d/pop-to-EBP - 197 c3/return - 198 - 199 test-convert-next-octet: - 200 # - check that the first two bytes of the input are assembled into the resulting octet - 201 # This test uses exit-descriptors. Use EBP for setting up local variables. - 202 55/push-EBP - 203 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 204 # clear all streams - 205 # . clear-stream(_test-stream) - 206 # . . push args - 207 68/push _test-stream/imm32 - 208 # . . call - 209 e8/call clear-stream/disp32 - 210 # . . discard args - 211 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 212 # . clear-stream(_test-buffered-file+4) - 213 # . . push args - 214 b8/copy-to-EAX _test-buffered-file/imm32 - 215 05/add-to-EAX 4/imm32 - 216 50/push-EAX - 217 # . . call - 218 e8/call clear-stream/disp32 - 219 # . . discard args - 220 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 221 # . clear-stream(_test-error-stream) - 222 # . . push args - 223 68/push _test-error-stream/imm32 - 224 # . . call - 225 e8/call clear-stream/disp32 - 226 # . . discard args - 227 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 228 # . clear-stream(_test-error-buffered-file+4) - 229 # . . push args - 230 b8/copy-to-EAX _test-error-buffered-file/imm32 - 231 05/add-to-EAX 4/imm32 - 232 50/push-EAX - 233 # . . call - 234 e8/call clear-stream/disp32 - 235 # . . discard args - 236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 237 # initialize '_test-stream' to "abc" - 238 # . write(_test-stream, "abc") - 239 # . . push args - 240 68/push "abc"/imm32 - 241 68/push _test-stream/imm32 - 242 # . . call - 243 e8/call write/disp32 - 244 # . . discard args - 245 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 246 # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below - 247 # . var ed/ECX : exit-descriptor - 248 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 249 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 250 # . tailor-exit-descriptor(ed, 12) - 251 # . . push args - 252 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet - 253 51/push-ECX/ed - 254 # . . call - 255 e8/call tailor-exit-descriptor/disp32 - 256 # . . discard args - 257 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 258 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) - 259 # . . push args - 260 51/push-ECX/ed - 261 68/push _test-error-buffered-file/imm32 - 262 68/push _test-buffered-file/imm32 - 263 # . . call - 264 e8/call convert-next-octet/disp32 - 265 # registers except ESP may be clobbered at this point - 266 # pop args to convert-next-octet - 267 # . . discard first 2 args - 268 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 269 # . . restore ed - 270 59/pop-to-ECX - 271 # check that convert-next-octet didn't abort - 272 # . check-ints-equal(ed->value, 0, msg) - 273 # . . push args - 274 68/push "F - test-convert-next-octet: unexpected abort"/imm32 - 275 68/push 0/imm32 - 276 # . . push ed->value - 277 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - 278 # . . call - 279 e8/call check-ints-equal/disp32 - 280 # . . discard args - 281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 282 # return if abort - 283 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 284 75/jump-if-not-equal $test-convert-next-octet:end/disp8 - 285 # check-ints-equal(EAX, 0xab, msg) - 286 # . . push args - 287 68/push "F - test-convert-next-octet"/imm32 - 288 68/push 0xab/imm32/ab - 289 50/push-EAX - 290 # . . call - 291 e8/call check-ints-equal/disp32 - 292 # . . discard args - 293 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 294 $test-convert-next-octet:end: - 295 # . epilog - 296 # don't restore ESP from EBP; manually reclaim locals - 297 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 298 5d/pop-to-EBP - 299 c3/return - 300 - 301 test-convert-next-octet-handles-Eof: - 302 # - check that reaching end of file returns Eof - 303 # This test uses exit-descriptors. Use EBP for setting up local variables. - 304 55/push-EBP - 305 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 306 # clear all streams - 307 # . clear-stream(_test-stream) - 308 # . . push args - 309 68/push _test-stream/imm32 - 310 # . . call - 311 e8/call clear-stream/disp32 - 312 # . . discard args - 313 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 314 # . clear-stream(_test-buffered-file+4) - 315 # . . push args - 316 b8/copy-to-EAX _test-buffered-file/imm32 - 317 05/add-to-EAX 4/imm32 - 318 50/push-EAX - 319 # . . call - 320 e8/call clear-stream/disp32 - 321 # . . discard args - 322 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 323 # . clear-stream(_test-error-stream) - 324 # . . push args - 325 68/push _test-error-stream/imm32 - 326 # . . call - 327 e8/call clear-stream/disp32 - 328 # . . discard args - 329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 330 # . clear-stream(_test-error-buffered-file+4) - 331 # . . push args - 332 b8/copy-to-EAX _test-error-buffered-file/imm32 - 333 05/add-to-EAX 4/imm32 - 334 50/push-EAX - 335 # . . call - 336 e8/call clear-stream/disp32 - 337 # . . discard args - 338 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 339 # don't initialize '_test-stream' - 340 # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below - 341 # . var ed/ECX : exit-descriptor - 342 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 343 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 344 # . tailor-exit-descriptor(ed, 12) - 345 # . . push args - 346 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet - 347 51/push-ECX/ed - 348 # . . call - 349 e8/call tailor-exit-descriptor/disp32 - 350 # . . discard args - 351 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 352 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) - 353 # . . push args - 354 51/push-ECX/ed - 355 68/push _test-error-buffered-file/imm32 - 356 68/push _test-buffered-file/imm32 - 357 # . . call - 358 e8/call convert-next-octet/disp32 - 359 # registers except ESP may be clobbered at this point - 360 # pop args to convert-next-octet - 361 # . . discard first 2 args - 362 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 363 # . . restore ed - 364 59/pop-to-ECX - 365 # check that convert-next-octet didn't abort - 366 # . check-ints-equal(ed->value, 0, msg) - 367 # . . push args - 368 68/push "F - test-convert-next-octet: unexpected abort"/imm32 - 369 68/push 0/imm32 - 370 # . . push ed->value - 371 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - 372 # . . call - 373 e8/call check-ints-equal/disp32 - 374 # . . discard args - 375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 376 # return if abort - 377 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 378 75/jump-if-not-equal $test-convert-next-octet-handles-Eof:end/disp8 - 379 # check-ints-equal(EAX, Eof, msg) - 380 # . . push args - 381 68/push "F - test-convert-next-octet-handles-Eof"/imm32 - 382 68/push 0xffffffff/imm32/Eof - 383 50/push-EAX - 384 # . . call - 385 e8/call check-ints-equal/disp32 - 386 # . . discard args - 387 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 388 $test-convert-next-octet-handles-Eof:end: - 389 # . epilog - 390 # don't restore ESP from EBP; manually reclaim locals - 391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 392 5d/pop-to-EBP - 393 c3/return - 394 - 395 test-convert-next-octet-aborts-on-single-hex-byte: - 396 # - check that a single unaccompanied hex byte aborts - 397 # This test uses exit-descriptors. Use EBP for setting up local variables. - 398 55/push-EBP - 399 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 400 # clear all streams - 401 # . clear-stream(_test-stream) - 402 # . . push args - 403 68/push _test-stream/imm32 - 404 # . . call - 405 e8/call clear-stream/disp32 - 406 # . . discard args - 407 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 408 # . clear-stream(_test-buffered-file+4) - 409 # . . push args - 410 b8/copy-to-EAX _test-buffered-file/imm32 - 411 05/add-to-EAX 4/imm32 - 412 50/push-EAX - 413 # . . call - 414 e8/call clear-stream/disp32 - 415 # . . discard args - 416 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 417 # . clear-stream(_test-error-stream) - 418 # . . push args - 419 68/push _test-error-stream/imm32 - 420 # . . call - 421 e8/call clear-stream/disp32 - 422 # . . discard args - 423 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 424 # . clear-stream(_test-error-buffered-file+4) - 425 # . . push args - 426 b8/copy-to-EAX _test-error-buffered-file/imm32 - 427 05/add-to-EAX 4/imm32 - 428 50/push-EAX - 429 # . . call - 430 e8/call clear-stream/disp32 - 431 # . . discard args - 432 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 433 # initialize '_test-stream' to "a" - 434 # . write(_test-stream, "a") - 435 # . . push args - 436 68/push "a"/imm32 - 437 68/push _test-stream/imm32 - 438 # . . call - 439 e8/call write/disp32 - 440 # . . discard args - 441 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 442 # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below - 443 # . var ed/ECX : exit-descriptor - 444 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 445 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 446 # . tailor-exit-descriptor(ed, 12) - 447 # . . push args - 448 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet - 449 51/push-ECX/ed - 450 # . . call - 451 e8/call tailor-exit-descriptor/disp32 - 452 # . . discard args - 453 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 454 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) - 455 # . . push args - 456 51/push-ECX/ed - 457 68/push _test-error-buffered-file/imm32 - 458 68/push _test-buffered-file/imm32 - 459 # . . call - 460 e8/call convert-next-octet/disp32 - 461 # registers except ESP may be clobbered at this point - 462 # pop args to convert-next-octet - 463 # . . discard first 2 args - 464 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 465 # . . restore ed - 466 59/pop-to-ECX - 467 # check that convert-next-octet aborted - 468 # . check-ints-equal(ed->value, 2, msg) - 469 # . . push args - 470 68/push "F - test-convert-next-octet-aborts-on-single-hex-byte: unexpected abort"/imm32 - 471 68/push 2/imm32 - 472 # . . push ed->value - 473 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - 474 # . . call - 475 e8/call check-ints-equal/disp32 - 476 # . . discard args - 477 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 478 $test-convert-next-octet-aborts-on-single-hex-byte:end: - 479 # . epilog - 480 # don't restore ESP from EBP; manually reclaim locals - 481 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 482 5d/pop-to-EBP - 483 c3/return - 484 - 485 # read whitespace until a hex byte, and return it - 486 # return Eof if file ends without finding a hex byte - 487 # on '#' skip all bytes until newline - 488 # abort on any other byte - 489 scan-next-byte: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX - 490 # pseudocode: - 491 # while true - 492 # EAX = read-byte-buffered(in) - 493 # if (EAX == Eof) return EAX - 494 # if (is-hex-digit?(EAX)) return EAX - 495 # if (EAX == ' ' or '\t' or '\n') continue - 496 # if (EAX == '#') skip-until-newline(in) - 497 # else error-byte(ed, err, "invalid byte: " EAX) - 498 # - 499 # . prolog - 500 55/push-EBP - 501 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 502 # . save registers - 503 $scan-next-byte:loop: - 504 # EAX = read-byte-buffered(in) - 505 # . . push args - 506 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 507 # . . call - 508 e8/call read-byte-buffered/disp32 - 509 # . . discard args - 510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 511 # if (EAX == Eof) return EAX - 512 3d/compare-with-EAX 0xffffffff/imm32/Eof - 513 74/jump-if-equal $scan-next-byte:end/disp8 - 514 # if (is-hex-digit?(EAX)) return EAX - 515 # . save EAX for now - 516 50/push-EAX - 517 # . is-hex-digit?(EAX) - 518 # . . push args - 519 50/push-EAX - 520 # . . call - 521 e8/call is-hex-digit?/disp32 - 522 # . . discard args - 523 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 524 # . compare with 'false' - 525 3d/compare-with-EAX 0/imm32 - 526 # . restore EAX (does not affect flags) - 527 58/pop-to-EAX - 528 # . check whether to return - 529 75/jump-if-not-equal $scan-next-byte:end/disp8 - 530 $scan-next-byte:check1: - 531 # if (EAX == ' ') continue - 532 3d/compare-EAX-and 0x20/imm32/space - 533 74/jump-if-equal $scan-next-byte:loop/disp8 - 534 # if (EAX == '\t') continue - 535 3d/compare-EAX-and 9/imm32/tab - 536 74/jump-if-equal $scan-next-byte:loop/disp8 - 537 # if (EAX == '\n') continue - 538 3d/compare-EAX-and 0xa/imm32/newline - 539 74/jump-if-equal $scan-next-byte:loop/disp8 - 540 $scan-next-byte:check2: - 541 # if (EAX == '#') skip-until-newline(in) - 542 3d/compare-with-EAX 0x23/imm32 - 543 75/jump-if-not-equal $scan-next-byte:check3/disp8 - 544 # . skip-until-newline(in) - 545 # . . push args - 546 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 547 # . . call - 548 e8/call skip-until-newline/disp32 - 549 # . . discard args - 550 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 551 eb/jump $scan-next-byte:loop/disp8 - 552 $scan-next-byte:check3: - 553 # otherwise error-byte(ed, err, msg, EAX) - 554 # . . push args - 555 50/push-EAX - 556 68/push "scan-next-byte: invalid byte"/imm32 - 557 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 558 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 559 # . . call - 560 e8/call error-byte/disp32 # never returns - 561 $scan-next-byte:end: - 562 # . restore registers - 563 # . epilog - 564 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 565 5d/pop-to-EBP - 566 c3/return - 567 - 568 test-scan-next-byte: - 569 # - check that the first byte of the input is returned - 570 # This test uses exit-descriptors. Use EBP for setting up local variables. - 571 55/push-EBP - 572 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 573 # clear all streams - 574 # . clear-stream(_test-stream) - 575 # . . push args - 576 68/push _test-stream/imm32 - 577 # . . call - 578 e8/call clear-stream/disp32 - 579 # . . discard args - 580 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 581 # . clear-stream(_test-buffered-file+4) - 582 # . . push args - 583 b8/copy-to-EAX _test-buffered-file/imm32 - 584 05/add-to-EAX 4/imm32 - 585 50/push-EAX - 586 # . . call - 587 e8/call clear-stream/disp32 - 588 # . . discard args - 589 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 590 # . clear-stream(_test-error-stream) - 591 # . . push args - 592 68/push _test-error-stream/imm32 - 593 # . . call - 594 e8/call clear-stream/disp32 - 595 # . . discard args - 596 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 597 # . clear-stream(_test-error-buffered-file+4) - 598 # . . push args - 599 b8/copy-to-EAX _test-error-buffered-file/imm32 - 600 05/add-to-EAX 4/imm32 - 601 50/push-EAX - 602 # . . call - 603 e8/call clear-stream/disp32 - 604 # . . discard args - 605 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 606 # initialize '_test-stream' to "abc" - 607 # . write(_test-stream, "abc") - 608 # . . push args - 609 68/push "abc"/imm32 - 610 68/push _test-stream/imm32 - 611 # . . call - 612 e8/call write/disp32 - 613 # . . discard args - 614 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 615 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - 616 # . var ed/ECX : exit-descriptor - 617 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 618 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 619 # . tailor-exit-descriptor(ed, 12) - 620 # . . push args - 621 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 622 51/push-ECX/ed - 623 # . . call - 624 e8/call tailor-exit-descriptor/disp32 - 625 # . . discard args - 626 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 627 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - 628 # . . push args - 629 51/push-ECX/ed - 630 68/push _test-error-buffered-file/imm32 - 631 68/push _test-buffered-file/imm32 - 632 # . . call - 633 e8/call scan-next-byte/disp32 - 634 # registers except ESP may be clobbered at this point - 635 # pop args to scan-next-byte - 636 # . . discard first 2 args - 637 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 638 # . . restore ed - 639 59/pop-to-ECX - 640 # check that scan-next-byte didn't abort - 641 # . check-ints-equal(ed->value, 0, msg) - 642 # . . push args - 643 68/push "F - test-scan-next-byte: unexpected abort"/imm32 - 644 68/push 0/imm32 - 645 # . . push ed->value - 646 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - 647 # . . call - 648 e8/call check-ints-equal/disp32 - 649 # . . discard args - 650 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 651 # return if abort - 652 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 653 75/jump-if-not-equal $test-scan-next-byte:end/disp8 - 654 # check-ints-equal(EAX, 0x61/a, msg) - 655 # . . push args - 656 68/push "F - test-scan-next-byte"/imm32 - 657 68/push 0x61/imm32/a - 658 50/push-EAX - 659 # . . call - 660 e8/call check-ints-equal/disp32 - 661 # . . discard args - 662 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 663 $test-scan-next-byte:end: - 664 # . epilog - 665 # don't restore ESP from EBP; manually reclaim locals - 666 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 667 5d/pop-to-EBP - 668 c3/return - 669 - 670 test-scan-next-byte-skips-whitespace: - 671 # - check that the first byte after whitespace is returned - 672 # This test uses exit-descriptors. Use EBP for setting up local variables. - 673 55/push-EBP - 674 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 675 # clear all streams - 676 # . clear-stream(_test-stream) - 677 # . . push args - 678 68/push _test-stream/imm32 - 679 # . . call - 680 e8/call clear-stream/disp32 - 681 # . . discard args - 682 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 683 # . clear-stream(_test-buffered-file+4) - 684 # . . push args - 685 b8/copy-to-EAX _test-buffered-file/imm32 - 686 05/add-to-EAX 4/imm32 - 687 50/push-EAX - 688 # . . call - 689 e8/call clear-stream/disp32 - 690 # . . discard args - 691 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 692 # . clear-stream(_test-error-stream) - 693 # . . push args - 694 68/push _test-error-stream/imm32 - 695 # . . call - 696 e8/call clear-stream/disp32 - 697 # . . discard args - 698 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 699 # . clear-stream(_test-error-buffered-file+4) - 700 # . . push args - 701 b8/copy-to-EAX _test-error-buffered-file/imm32 - 702 05/add-to-EAX 4/imm32 - 703 50/push-EAX - 704 # . . call - 705 e8/call clear-stream/disp32 - 706 # . . discard args - 707 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 708 # initialize '_test-stream' to input with leading whitespace - 709 # . write(_test-stream, text) - 710 # . . push args - 711 68/push " abc"/imm32 - 712 68/push _test-stream/imm32 - 713 # . . call - 714 e8/call write/disp32 - 715 # . . discard args - 716 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 717 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - 718 # . var ed/ECX : exit-descriptor - 719 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 720 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 721 # . tailor-exit-descriptor(ed, 12) - 722 # . . push args - 723 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 724 51/push-ECX/ed - 725 # . . call - 726 e8/call tailor-exit-descriptor/disp32 - 727 # . . discard args - 728 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 729 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - 730 # . . push args - 731 51/push-ECX/ed - 732 68/push _test-error-buffered-file/imm32 - 733 68/push _test-buffered-file/imm32 - 734 # . . call - 735 e8/call scan-next-byte/disp32 - 736 # registers except ESP may be clobbered at this point - 737 # pop args to scan-next-byte - 738 # . . discard first 2 args - 739 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 740 # . . restore ed - 741 59/pop-to-ECX - 742 # check that scan-next-byte didn't abort - 743 # . check-ints-equal(ed->value, 0, msg) - 744 # . . push args - 745 68/push "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32 - 746 68/push 0/imm32 - 747 # . . push ed->value - 748 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - 749 # . . call - 750 e8/call check-ints-equal/disp32 - 751 # . . discard args - 752 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 753 # return if abort - 754 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 755 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace:end/disp8 - 756 # check-ints-equal(EAX, 0x61/a, msg) - 757 # . . push args - 758 68/push "F - test-scan-next-byte-skips-whitespace"/imm32 - 759 68/push 0x61/imm32/a - 760 50/push-EAX - 761 # . . call - 762 e8/call check-ints-equal/disp32 - 763 # . . discard args - 764 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 765 $test-scan-next-byte-skips-whitespace:end: - 766 # . epilog - 767 # don't restore ESP from EBP; manually reclaim locals - 768 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 769 5d/pop-to-EBP - 770 c3/return - 771 - 772 test-scan-next-byte-skips-comment: - 773 # - check that the first byte after a comment (and newline) is returned - 774 # This test uses exit-descriptors. Use EBP for setting up local variables. - 775 55/push-EBP - 776 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 777 # clear all streams - 778 # . clear-stream(_test-stream) - 779 # . . push args - 780 68/push _test-stream/imm32 - 781 # . . call - 782 e8/call clear-stream/disp32 - 783 # . . discard args - 784 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 785 # . clear-stream(_test-buffered-file+4) - 786 # . . push args - 787 b8/copy-to-EAX _test-buffered-file/imm32 - 788 05/add-to-EAX 4/imm32 - 789 50/push-EAX - 790 # . . call - 791 e8/call clear-stream/disp32 - 792 # . . discard args - 793 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 794 # . clear-stream(_test-error-stream) - 795 # . . push args - 796 68/push _test-error-stream/imm32 - 797 # . . call - 798 e8/call clear-stream/disp32 - 799 # . . discard args - 800 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 801 # . clear-stream(_test-error-buffered-file+4) - 802 # . . push args - 803 b8/copy-to-EAX _test-error-buffered-file/imm32 - 804 05/add-to-EAX 4/imm32 - 805 50/push-EAX - 806 # . . call - 807 e8/call clear-stream/disp32 - 808 # . . discard args - 809 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 810 # initialize '_test-stream' to input with leading comment - 811 # . write(_test-stream, comment) - 812 # . . push args - 813 68/push "#x\n"/imm32 - 814 68/push _test-stream/imm32 - 815 # . . call - 816 e8/call write/disp32 - 817 # . . discard args - 818 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 819 # . write(_test-stream, real text) - 820 # . . push args - 821 68/push "ab"/imm32 - 822 68/push _test-stream/imm32 - 823 # . . call - 824 e8/call write/disp32 - 825 # . . discard args - 826 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 827 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - 828 # . var ed/ECX : exit-descriptor - 829 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 830 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 831 # . tailor-exit-descriptor(ed, 12) - 832 # . . push args - 833 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 834 51/push-ECX/ed - 835 # . . call - 836 e8/call tailor-exit-descriptor/disp32 - 837 # . . discard args - 838 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 839 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - 840 # . . push args - 841 51/push-ECX/ed - 842 68/push _test-error-buffered-file/imm32 - 843 68/push _test-buffered-file/imm32 - 844 # . . call - 845 e8/call scan-next-byte/disp32 - 846 # registers except ESP may be clobbered at this point - 847 # pop args to scan-next-byte - 848 # . . discard first 2 args - 849 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 850 # . . restore ed - 851 59/pop-to-ECX - 852 # check that scan-next-byte didn't abort - 853 # . check-ints-equal(ed->value, 0, msg) - 854 # . . push args - 855 68/push "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32 - 856 68/push 0/imm32 - 857 # . . push ed->value - 858 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - 859 # . . call - 860 e8/call check-ints-equal/disp32 - 861 # . . discard args - 862 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 863 # return if abort - 864 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 865 75/jump-if-not-equal $test-scan-next-byte-skips-comment:end/disp8 - 866 # check-ints-equal(EAX, 0x61/a, msg) - 867 # . . push args - 868 68/push "F - test-scan-next-byte-skips-comment"/imm32 - 869 68/push 0x61/imm32/a - 870 50/push-EAX - 871 # . . call - 872 e8/call check-ints-equal/disp32 - 873 # . . discard args - 874 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 875 $test-scan-next-byte-skips-comment:end: - 876 # . epilog - 877 # don't restore ESP from EBP; manually reclaim locals - 878 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 879 5d/pop-to-EBP - 880 c3/return - 881 - 882 test-scan-next-byte-skips-comment-and-whitespace: - 883 # - check that the first byte after a comment and any further whitespace is returned - 884 # This test uses exit-descriptors. Use EBP for setting up local variables. - 885 55/push-EBP - 886 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 887 # clear all streams - 888 # . clear-stream(_test-stream) - 889 # . . push args - 890 68/push _test-stream/imm32 - 891 # . . call - 892 e8/call clear-stream/disp32 - 893 # . . discard args - 894 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 895 # . clear-stream(_test-buffered-file+4) - 896 # . . push args - 897 b8/copy-to-EAX _test-buffered-file/imm32 - 898 05/add-to-EAX 4/imm32 - 899 50/push-EAX - 900 # . . call - 901 e8/call clear-stream/disp32 - 902 # . . discard args - 903 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 904 # . clear-stream(_test-error-stream) - 905 # . . push args - 906 68/push _test-error-stream/imm32 - 907 # . . call - 908 e8/call clear-stream/disp32 - 909 # . . discard args - 910 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 911 # . clear-stream(_test-error-buffered-file+4) - 912 # . . push args - 913 b8/copy-to-EAX _test-error-buffered-file/imm32 - 914 05/add-to-EAX 4/imm32 - 915 50/push-EAX - 916 # . . call - 917 e8/call clear-stream/disp32 - 918 # . . discard args - 919 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 920 # initialize '_test-stream' to input with leading comment and more whitespace after newline - 921 # . write(_test-stream, comment) - 922 # . . push args - 923 68/push "#x\n"/imm32 - 924 68/push _test-stream/imm32 - 925 # . . call - 926 e8/call write/disp32 - 927 # . . discard args - 928 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 929 # . write(_test-stream, real text) - 930 # . . push args - 931 68/push " ab"/imm32 - 932 68/push _test-stream/imm32 - 933 # . . call - 934 e8/call write/disp32 - 935 # . . discard args - 936 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 937 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - 938 # . var ed/ECX : exit-descriptor - 939 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 940 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 941 # . tailor-exit-descriptor(ed, 12) - 942 # . . push args - 943 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 944 51/push-ECX/ed - 945 # . . call - 946 e8/call tailor-exit-descriptor/disp32 - 947 # . . discard args - 948 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 949 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - 950 # . . push args - 951 51/push-ECX/ed - 952 68/push _test-error-buffered-file/imm32 - 953 68/push _test-buffered-file/imm32 - 954 # . . call - 955 e8/call scan-next-byte/disp32 - 956 # registers except ESP may be clobbered at this point - 957 # pop args to scan-next-byte - 958 # . . discard first 2 args - 959 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 960 # . . restore ed - 961 59/pop-to-ECX - 962 # check that scan-next-byte didn't abort - 963 # . check-ints-equal(ed->value, 0, msg) - 964 # . . push args - 965 68/push "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32 - 966 68/push 0/imm32 - 967 # . . push ed->value - 968 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - 969 # . . call - 970 e8/call check-ints-equal/disp32 - 971 # . . discard args - 972 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 973 # return if abort - 974 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 975 75/jump-if-not-equal $test-scan-next-byte-skips-comment-and-whitespace:end/disp8 - 976 # check-ints-equal(EAX, 0x61/a, msg) - 977 # . . push args - 978 68/push "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32 - 979 68/push 0x61/imm32/a - 980 50/push-EAX - 981 # . . call - 982 e8/call check-ints-equal/disp32 - 983 # . . discard args - 984 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 985 $test-scan-next-byte-skips-comment-and-whitespace:end: - 986 # . epilog - 987 # don't restore ESP from EBP; manually reclaim locals - 988 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 989 5d/pop-to-EBP - 990 c3/return - 991 - 992 test-scan-next-byte-skips-whitespace-and-comment: - 993 # - check that the first byte after any whitespace and comments is returned - 994 # This test uses exit-descriptors. Use EBP for setting up local variables. - 995 55/push-EBP - 996 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 997 # clear all streams - 998 # . clear-stream(_test-stream) - 999 # . . push args -1000 68/push _test-stream/imm32 -1001 # . . call -1002 e8/call clear-stream/disp32 -1003 # . . discard args -1004 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1005 # . clear-stream(_test-buffered-file+4) -1006 # . . push args -1007 b8/copy-to-EAX _test-buffered-file/imm32 -1008 05/add-to-EAX 4/imm32 -1009 50/push-EAX -1010 # . . call -1011 e8/call clear-stream/disp32 -1012 # . . discard args -1013 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1014 # . clear-stream(_test-error-stream) -1015 # . . push args -1016 68/push _test-error-stream/imm32 -1017 # . . call -1018 e8/call clear-stream/disp32 -1019 # . . discard args -1020 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1021 # . clear-stream(_test-error-buffered-file+4) -1022 # . . push args -1023 b8/copy-to-EAX _test-error-buffered-file/imm32 -1024 05/add-to-EAX 4/imm32 -1025 50/push-EAX -1026 # . . call -1027 e8/call clear-stream/disp32 -1028 # . . discard args -1029 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1030 # initialize '_test-stream' to input with leading whitespace and comment -1031 # . write(_test-stream, comment) -1032 # . . push args -1033 68/push " #x\n"/imm32 -1034 68/push _test-stream/imm32 -1035 # . . call -1036 e8/call write/disp32 -1037 # . . discard args -1038 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1039 # . write(_test-stream, real text) -1040 # . . push args -1041 68/push "ab"/imm32 -1042 68/push _test-stream/imm32 -1043 # . . call -1044 e8/call write/disp32 -1045 # . . discard args -1046 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1047 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below -1048 # . var ed/ECX : exit-descriptor -1049 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -1050 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1051 # . tailor-exit-descriptor(ed, 12) -1052 # . . push args -1053 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte -1054 51/push-ECX/ed -1055 # . . call -1056 e8/call tailor-exit-descriptor/disp32 -1057 # . . discard args -1058 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1059 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) -1060 # . . push args -1061 51/push-ECX/ed -1062 68/push _test-error-buffered-file/imm32 -1063 68/push _test-buffered-file/imm32 -1064 # . . call -1065 e8/call scan-next-byte/disp32 -1066 # registers except ESP may be clobbered at this point -1067 # pop args to scan-next-byte -1068 # . . discard first 2 args -1069 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1070 # . . restore ed -1071 59/pop-to-ECX -1072 # check that scan-next-byte didn't abort -1073 # . check-ints-equal(ed->value, 0, msg) -1074 # . . push args -1075 68/push "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32 -1076 68/push 0/imm32 -1077 # . . push ed->value -1078 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) -1079 # . . call -1080 e8/call check-ints-equal/disp32 -1081 # . . discard args -1082 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1083 # return if abort -1084 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) -1085 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace-and-comment:end/disp8 -1086 # check-ints-equal(EAX, 0x61/a, msg) -1087 # . . push args -1088 68/push "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32 -1089 68/push 0x61/imm32/a -1090 50/push-EAX -1091 # . . call -1092 e8/call check-ints-equal/disp32 -1093 # . . discard args -1094 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1095 $test-scan-next-byte-skips-whitespace-and-comment:end: -1096 # . epilog -1097 # don't restore ESP from EBP; manually reclaim locals -1098 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1099 5d/pop-to-EBP -1100 c3/return -1101 -1102 test-scan-next-byte-reads-final-byte: -1103 # - check that the final byte in input is returned -1104 # This test uses exit-descriptors. Use EBP for setting up local variables. -1105 55/push-EBP -1106 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1107 # clear all streams -1108 # . clear-stream(_test-stream) -1109 # . . push args -1110 68/push _test-stream/imm32 -1111 # . . call -1112 e8/call clear-stream/disp32 -1113 # . . discard args -1114 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1115 # . clear-stream(_test-buffered-file+4) -1116 # . . push args -1117 b8/copy-to-EAX _test-buffered-file/imm32 -1118 05/add-to-EAX 4/imm32 -1119 50/push-EAX -1120 # . . call -1121 e8/call clear-stream/disp32 -1122 # . . discard args -1123 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1124 # . clear-stream(_test-error-stream) -1125 # . . push args -1126 68/push _test-error-stream/imm32 -1127 # . . call -1128 e8/call clear-stream/disp32 -1129 # . . discard args -1130 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1131 # . clear-stream(_test-error-buffered-file+4) -1132 # . . push args -1133 b8/copy-to-EAX _test-error-buffered-file/imm32 -1134 05/add-to-EAX 4/imm32 -1135 50/push-EAX -1136 # . . call -1137 e8/call clear-stream/disp32 -1138 # . . discard args -1139 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1140 # initialize '_test-stream' to input with single character -1141 # . write(_test-stream, character) -1142 # . . push args -1143 68/push "a"/imm32 -1144 68/push _test-stream/imm32 -1145 # . . call -1146 e8/call write/disp32 -1147 # . . discard args -1148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1149 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below -1150 # . var ed/ECX : exit-descriptor -1151 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -1152 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1153 # . tailor-exit-descriptor(ed, 12) -1154 # . . push args -1155 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte -1156 51/push-ECX/ed -1157 # . . call -1158 e8/call tailor-exit-descriptor/disp32 -1159 # . . discard args -1160 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1161 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) -1162 # . . push args -1163 51/push-ECX/ed -1164 68/push _test-error-buffered-file/imm32 -1165 68/push _test-buffered-file/imm32 -1166 # . . call -1167 e8/call scan-next-byte/disp32 -1168 # registers except ESP may be clobbered at this point -1169 # pop args to scan-next-byte -1170 # . . discard first 2 args -1171 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1172 # . . restore ed -1173 59/pop-to-ECX -1174 # check that scan-next-byte didn't abort -1175 # . check-ints-equal(ed->value, 0, msg) -1176 # . . push args -1177 68/push "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32 -1178 68/push 0/imm32 -1179 # . . push ed->value -1180 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) -1181 # . . call -1182 e8/call check-ints-equal/disp32 -1183 # . . discard args -1184 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1185 # return if abort -1186 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) -1187 75/jump-if-not-equal $test-scan-next-byte-reads-final-byte:end/disp8 -1188 # check-ints-equal(EAX, 0x61/a, msg) -1189 # . . push args -1190 68/push "F - test-scan-next-byte-reads-final-byte"/imm32 -1191 68/push 0x61/imm32/a -1192 50/push-EAX -1193 # . . call -1194 e8/call check-ints-equal/disp32 -1195 # . . discard args -1196 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1197 $test-scan-next-byte-reads-final-byte:end: -1198 # . epilog -1199 # don't restore ESP from EBP; manually reclaim locals -1200 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1201 5d/pop-to-EBP -1202 c3/return -1203 -1204 test-scan-next-byte-handles-Eof: -1205 # - check that the right sentinel value is returned when there's no data remaining to be read -1206 # This test uses exit-descriptors. Use EBP for setting up local variables. -1207 55/push-EBP -1208 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1209 # clear all streams -1210 # . clear-stream(_test-stream) -1211 # . . push args -1212 68/push _test-stream/imm32 -1213 # . . call -1214 e8/call clear-stream/disp32 -1215 # . . discard args -1216 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1217 # . clear-stream(_test-buffered-file+4) -1218 # . . push args -1219 b8/copy-to-EAX _test-buffered-file/imm32 -1220 05/add-to-EAX 4/imm32 -1221 50/push-EAX -1222 # . . call -1223 e8/call clear-stream/disp32 -1224 # . . discard args -1225 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1226 # . clear-stream(_test-error-stream) -1227 # . . push args -1228 68/push _test-error-stream/imm32 -1229 # . . call -1230 e8/call clear-stream/disp32 -1231 # . . discard args -1232 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1233 # . clear-stream(_test-error-buffered-file+4) -1234 # . . push args -1235 b8/copy-to-EAX _test-error-buffered-file/imm32 -1236 05/add-to-EAX 4/imm32 -1237 50/push-EAX -1238 # . . call -1239 e8/call clear-stream/disp32 -1240 # . . discard args -1241 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1242 # leave '_test-stream' empty -1243 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below -1244 # . var ed/ECX : exit-descriptor -1245 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -1246 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1247 # . tailor-exit-descriptor(ed, 12) -1248 # . . push args -1249 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte -1250 51/push-ECX/ed -1251 # . . call -1252 e8/call tailor-exit-descriptor/disp32 -1253 # . . discard args -1254 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1255 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) -1256 # . . push args -1257 51/push-ECX/ed -1258 68/push _test-error-buffered-file/imm32 -1259 68/push _test-buffered-file/imm32 -1260 # . . call -1261 e8/call scan-next-byte/disp32 -1262 # registers except ESP may be clobbered at this point -1263 # pop args to scan-next-byte -1264 # . . discard first 2 args -1265 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1266 # . . restore ed -1267 59/pop-to-ECX -1268 # check that scan-next-byte didn't abort -1269 # . check-ints-equal(ed->value, 0, msg) -1270 # . . push args -1271 68/push "F - test-scan-next-byte-handles-Eof: unexpected abort"/imm32 -1272 68/push 0/imm32 -1273 # . . push ed->value -1274 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) -1275 # . . call -1276 e8/call check-ints-equal/disp32 -1277 # . . discard args -1278 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1279 # return if abort -1280 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) -1281 75/jump-if-not-equal $test-scan-next-byte-handles-Eof:end/disp8 -1282 # check-ints-equal(EAX, Eof, msg) -1283 # . . push args -1284 68/push "F - test-scan-next-byte-handles-Eof"/imm32 -1285 68/push 0xffffffff/imm32/Eof -1286 50/push-EAX -1287 # . . call -1288 e8/call check-ints-equal/disp32 -1289 # . . discard args -1290 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1291 $test-scan-next-byte-handles-Eof:end: -1292 # . epilog -1293 # don't restore ESP from EBP; manually reclaim locals -1294 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1295 5d/pop-to-EBP -1296 c3/return -1297 -1298 test-scan-next-byte-aborts-on-invalid-byte: -1299 # - check that the a bad byte immediately aborts -1300 # This test uses exit-descriptors. Use EBP for setting up local variables. -1301 55/push-EBP -1302 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1303 # clear all streams -1304 # . clear-stream(_test-stream) -1305 # . . push args -1306 68/push _test-stream/imm32 -1307 # . . call -1308 e8/call clear-stream/disp32 -1309 # . . discard args -1310 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1311 # . clear-stream(_test-buffered-file+4) -1312 # . . push args -1313 b8/copy-to-EAX _test-buffered-file/imm32 -1314 05/add-to-EAX 4/imm32 -1315 50/push-EAX -1316 # . . call -1317 e8/call clear-stream/disp32 -1318 # . . discard args -1319 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1320 # . clear-stream(_test-error-stream) -1321 # . . push args -1322 68/push _test-error-stream/imm32 -1323 # . . call -1324 e8/call clear-stream/disp32 -1325 # . . discard args -1326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1327 # . clear-stream(_test-error-buffered-file+4) -1328 # . . push args -1329 b8/copy-to-EAX _test-error-buffered-file/imm32 -1330 05/add-to-EAX 4/imm32 -1331 50/push-EAX -1332 # . . call -1333 e8/call clear-stream/disp32 -1334 # . . discard args -1335 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1336 # initialize '_test-stream' to "x" -1337 # . write(_test-stream, "x") -1338 # . . push args -1339 68/push "x"/imm32 -1340 68/push _test-stream/imm32 -1341 # . . call -1342 e8/call write/disp32 -1343 # . . discard args -1344 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1345 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below -1346 # . var ed/ECX : exit-descriptor -1347 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -1348 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1349 # . tailor-exit-descriptor(ed, 12) -1350 # . . push args -1351 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte -1352 51/push-ECX/ed -1353 # . . call -1354 e8/call tailor-exit-descriptor/disp32 -1355 # . . discard args -1356 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1357 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) -1358 # . . push args -1359 51/push-ECX/ed -1360 68/push _test-error-buffered-file/imm32 -1361 68/push _test-buffered-file/imm32 -1362 # . . call -1363 e8/call scan-next-byte/disp32 -1364 # registers except ESP may be clobbered at this point -1365 # pop args to scan-next-byte -1366 # . . discard first 2 args -1367 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1368 # . . restore ed -1369 59/pop-to-ECX -1370 # check that scan-next-byte aborted -1371 # . check-ints-equal(ed->value, 2, msg) -1372 # . . push args -1373 68/push "F - test-scan-next-byte-aborts-on-invalid-byte"/imm32 -1374 68/push 2/imm32 -1375 # . . push ed->value -1376 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) -1377 # . . call -1378 e8/call check-ints-equal/disp32 -1379 # . . discard args -1380 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1381 $test-scan-next-byte-aborts-on-invalid-byte:end: -1382 # . epilog -1383 # don't restore ESP from EBP; manually reclaim locals -1384 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1385 5d/pop-to-EBP -1386 c3/return -1387 -1388 skip-until-newline: # in : (address buffered-file) -> <void> -1389 # pseudocode: -1390 # push EAX -1391 # while true -1392 # EAX = read-byte-buffered(in) -1393 # if (EAX == Eof) break -1394 # if (EAX == 0x0a) break -1395 # pop EAX -1396 # . prolog -1397 55/push-EBP -1398 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1399 # . save registers -1400 50/push-EAX -1401 $skip-until-newline:loop: -1402 # . EAX = read-byte-buffered(in) -1403 # . . push args -1404 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1405 # . . call -1406 e8/call read-byte-buffered/disp32 -1407 # . . discard args -1408 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1409 # . if (EAX == Eof) break -1410 3d/compare-EAX-and 0xffffffff/imm32/Eof -1411 74/jump-if-equal $skip-until-newline:end/disp8 -1412 # . if (EAX != 0xa/newline) loop -1413 3d/compare-EAX-and 0xa/imm32/newline -1414 75/jump-if-not-equal $skip-until-newline:loop/disp8 -1415 $skip-until-newline:end: -1416 # . restore registers -1417 58/pop-to-EAX -1418 # . epilog -1419 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1420 5d/pop-to-EBP -1421 c3/return -1422 -1423 test-skip-until-newline: -1424 # - check that the read pointer points after the newline -1425 # setup -1426 # . clear-stream(_test-stream) -1427 # . . push args -1428 68/push _test-stream/imm32 -1429 # . . call -1430 e8/call clear-stream/disp32 -1431 # . . discard args -1432 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1433 # . clear-stream(_test-buffered-file+4) -1434 # . . push args -1435 b8/copy-to-EAX _test-buffered-file/imm32 -1436 05/add-to-EAX 4/imm32 -1437 50/push-EAX -1438 # . . call -1439 e8/call clear-stream/disp32 -1440 # . . discard args -1441 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1442 # initialize '_test-stream' to "abc\nde" -1443 # . write(_test-stream, "abc") -1444 # . . push args -1445 68/push "abc\n"/imm32 -1446 68/push _test-stream/imm32 -1447 # . . call -1448 e8/call write/disp32 -1449 # . . discard args -1450 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1451 # . write(_test-stream, "de") -1452 # . . push args -1453 68/push "de"/imm32 -1454 68/push _test-stream/imm32 -1455 # . . call -1456 e8/call write/disp32 -1457 # . . discard args -1458 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1459 # skip-until-newline(_test-buffered-file) -1460 # . . push args -1461 68/push _test-buffered-file/imm32 -1462 # . . call -1463 e8/call skip-until-newline/disp32 -1464 # . . discard args -1465 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1466 # check-ints-equal(_test-buffered-file->read, 4, msg) -1467 # . . push args -1468 68/push "F - test-skip-until-newline"/imm32 -1469 68/push 4/imm32 -1470 b8/copy-to-EAX _test-buffered-file/imm32 -1471 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8) -1472 # . . call -1473 e8/call check-ints-equal/disp32 -1474 # . . discard args -1475 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1476 # . end -1477 c3/return -1478 -1479 == data -1480 -1481 _test-error-stream: -1482 # current write index -1483 0/imm32 -1484 # current read index -1485 0/imm32 -1486 # line -1487 0x80/imm32 # 128 bytes -1488 # data (8 lines x 16 bytes/line) -1489 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -1490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -1491 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -1492 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 21 # initialize heap + 22 # . Heap = new-segment(64KB) + 23 # . . push args + 24 68/push Heap/imm32 + 25 68/push 0x10000/imm32/64KB + 26 # . . call + 27 e8/call new-segment/disp32 + 28 # . . discard args + 29 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 30 + 31 # . prolog + 32 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 33 # - if argc > 1 and argv[1] == "test", then return run_tests() + 34 # . argc > 1 + 35 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP + 36 7e/jump-if-lesser-or-equal $run-main/disp8 + 37 # . argv[1] == "test" + 38 # . . push args + 39 68/push "test"/imm32 + 40 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 41 # . . call + 42 e8/call kernel-string-equal?/disp32 + 43 # . . discard args + 44 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 45 # . check result + 46 3d/compare-EAX-and 1/imm32 + 47 75/jump-if-not-equal $run-main/disp8 + 48 # . run-tests() + 49 e8/call run-tests/disp32 + 50 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 51 eb/jump $main:end/disp8 + 52 $run-main: + 53 # - otherwise convert stdin + 54 # var ed/EAX : exit-descriptor + 55 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 56 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX + 57 # configure ed to really exit() + 58 # . ed->target = 0 + 59 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 60 # return convert(Stdin, 1/stdout, 2/stderr, ed) + 61 # . . push args + 62 50/push-EAX/ed + 63 68/push Stderr/imm32 + 64 68/push Stdout/imm32 + 65 68/push Stdin/imm32 + 66 # . . call + 67 e8/call convert/disp32 + 68 # . . discard args + 69 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 70 # . syscall(exit, 0) + 71 bb/copy-to-EBX 0/imm32 + 72 $main:end: + 73 b8/copy-to-EAX 1/imm32/exit + 74 cd/syscall 0x80/imm8 + 75 + 76 # the main entry point + 77 convert: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void> + 78 # pseudocode: + 79 # while true + 80 # EAX = convert-next-octet(in, err, ed) + 81 # if (EAX == Eof) break + 82 # write-byte-buffered(out, AL) + 83 # flush(out) + 84 # + 85 # . prolog + 86 55/push-EBP + 87 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 88 # . save registers + 89 50/push-EAX + 90 $convert:loop: + 91 # EAX = convert-next-octet(in, err, ed) + 92 # . . push args + 93 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) + 94 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 95 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 96 # . . call + 97 e8/call convert-next-octet/disp32 + 98 # . . discard first 2 args + 99 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 100 # if (EAX == Eof) break + 101 3d/compare-EAX-and 0xffffffff/imm32/Eof + 102 74/jump-if-equal $convert:loop-end/disp8 + 103 # write-byte-buffered(out, AL) + 104 # . . push args + 105 50/push-EAX + 106 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 107 # . . call + 108 e8/call write-byte-buffered/disp32 + 109 # . . discard args + 110 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 111 # loop + 112 eb/jump $convert:loop/disp8 + 113 $convert:loop-end: + 114 # flush(out) + 115 # . . push args + 116 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 117 # . . call + 118 e8/call flush/disp32 + 119 # . . discard args + 120 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 121 $convert:end: + 122 # . restore registers + 123 58/pop-to-EAX + 124 # . epilog + 125 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 126 5d/pop-to-EBP + 127 c3/return + 128 + 129 # read bytes from 'in' until a sequence of two lowercase hex (0-9, a-f) bytes + 130 # skip spaces and newlines + 131 # on '#' skip bytes until newline + 132 # raise an error and abort on all other unexpected bytes + 133 # return in EAX an _octet_ containing the binary value of the two hex characters + 134 # return Eof on reaching end of file + 135 convert-next-octet: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX + 136 # pseudocode: + 137 # EAX = scan-next-byte(in, err, ed) + 138 # if (EAX == Eof) return + 139 # ECX = from-hex-char(EAX) + 140 # EAX = scan-next-byte(in, err, ed) + 141 # if (EAX == Eof) error("partial byte found.") + 142 # EAX = from-hex-char(EAX) + 143 # EAX = (ECX << 4) | EAX + 144 # return + 145 # + 146 # . prolog + 147 55/push-EBP + 148 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 149 # . save registers + 150 51/push-ECX + 151 # EAX = scan-next-byte(in, err, ed) + 152 # . . push args + 153 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 154 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 155 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 156 # . . call + 157 e8/call scan-next-byte/disp32 + 158 # . . discard args + 159 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 160 # if (EAX == Eof) return + 161 3d/compare-EAX-and 0xffffffff/imm32/Eof + 162 74/jump-if-equal $convert-next-octet:end/disp8 + 163 # EAX = from-hex-char(EAX) + 164 e8/call from-hex-char/disp32 + 165 # ECX = EAX + 166 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX + 167 # EAX = scan-next-byte(in, err, ed) + 168 # . . push args + 169 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 170 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 171 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 172 # . . call + 173 e8/call scan-next-byte/disp32 + 174 # . . discard args + 175 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 176 # if (EAX == Eof) error(ed, err, "partial byte found.") + 177 3d/compare-EAX-and 0xffffffff/imm32/Eof + 178 75/jump-if-not-equal $convert-next-octet:convert/disp8 + 179 # . error-byte(ed, err, msg, '.') # reusing error-byte to avoid creating _yet_ another helper + 180 # . . push args + 181 68/push 0x2e/imm32/period/dummy + 182 68/push "convert-next-octet: partial byte found"/imm32 + 183 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 184 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 185 # . . call + 186 e8/call error-byte/disp32 # never returns + 187 $convert-next-octet:convert: + 188 # EAX = from-hex-char(EAX) + 189 e8/call from-hex-char/disp32 + 190 # EAX = (ECX << 4) | EAX + 191 # . ECX <<= 4 + 192 c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX . . . . . 4/imm8 # shift ECX left by 4 bits + 193 # . EAX |= ECX + 194 09/or 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # EAX = bitwise OR with ECX + 195 $convert-next-octet:end: + 196 # . restore registers + 197 59/pop-to-ECX + 198 # . epilog + 199 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 200 5d/pop-to-EBP + 201 c3/return + 202 + 203 test-convert-next-octet: + 204 # - check that the first two bytes of the input are assembled into the resulting octet + 205 # This test uses exit-descriptors. Use EBP for setting up local variables. + 206 55/push-EBP + 207 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 208 # clear all streams + 209 # . clear-stream(_test-stream) + 210 # . . push args + 211 68/push _test-stream/imm32 + 212 # . . call + 213 e8/call clear-stream/disp32 + 214 # . . discard args + 215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 216 # . clear-stream(_test-buffered-file+4) + 217 # . . push args + 218 b8/copy-to-EAX _test-buffered-file/imm32 + 219 05/add-to-EAX 4/imm32 + 220 50/push-EAX + 221 # . . call + 222 e8/call clear-stream/disp32 + 223 # . . discard args + 224 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 225 # . clear-stream(_test-error-stream) + 226 # . . push args + 227 68/push _test-error-stream/imm32 + 228 # . . call + 229 e8/call clear-stream/disp32 + 230 # . . discard args + 231 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 232 # . clear-stream(_test-error-buffered-file+4) + 233 # . . push args + 234 b8/copy-to-EAX _test-error-buffered-file/imm32 + 235 05/add-to-EAX 4/imm32 + 236 50/push-EAX + 237 # . . call + 238 e8/call clear-stream/disp32 + 239 # . . discard args + 240 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 241 # initialize '_test-stream' to "abc" + 242 # . write(_test-stream, "abc") + 243 # . . push args + 244 68/push "abc"/imm32 + 245 68/push _test-stream/imm32 + 246 # . . call + 247 e8/call write/disp32 + 248 # . . discard args + 249 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 250 # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below + 251 # . var ed/ECX : exit-descriptor + 252 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 253 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 254 # . tailor-exit-descriptor(ed, 12) + 255 # . . push args + 256 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet + 257 51/push-ECX/ed + 258 # . . call + 259 e8/call tailor-exit-descriptor/disp32 + 260 # . . discard args + 261 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 262 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) + 263 # . . push args + 264 51/push-ECX/ed + 265 68/push _test-error-buffered-file/imm32 + 266 68/push _test-buffered-file/imm32 + 267 # . . call + 268 e8/call convert-next-octet/disp32 + 269 # registers except ESP may be clobbered at this point + 270 # pop args to convert-next-octet + 271 # . . discard first 2 args + 272 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 273 # . . restore ed + 274 59/pop-to-ECX + 275 # check that convert-next-octet didn't abort + 276 # . check-ints-equal(ed->value, 0, msg) + 277 # . . push args + 278 68/push "F - test-convert-next-octet: unexpected abort"/imm32 + 279 68/push 0/imm32 + 280 # . . push ed->value + 281 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) + 282 # . . call + 283 e8/call check-ints-equal/disp32 + 284 # . . discard args + 285 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 286 # return if abort + 287 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) + 288 75/jump-if-not-equal $test-convert-next-octet:end/disp8 + 289 # check-ints-equal(EAX, 0xab, msg) + 290 # . . push args + 291 68/push "F - test-convert-next-octet"/imm32 + 292 68/push 0xab/imm32/ab + 293 50/push-EAX + 294 # . . call + 295 e8/call check-ints-equal/disp32 + 296 # . . discard args + 297 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 298 $test-convert-next-octet:end: + 299 # . epilog + 300 # don't restore ESP from EBP; manually reclaim locals + 301 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 302 5d/pop-to-EBP + 303 c3/return + 304 + 305 test-convert-next-octet-handles-Eof: + 306 # - check that reaching end of file returns Eof + 307 # This test uses exit-descriptors. Use EBP for setting up local variables. + 308 55/push-EBP + 309 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 310 # clear all streams + 311 # . clear-stream(_test-stream) + 312 # . . push args + 313 68/push _test-stream/imm32 + 314 # . . call + 315 e8/call clear-stream/disp32 + 316 # . . discard args + 317 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 318 # . clear-stream(_test-buffered-file+4) + 319 # . . push args + 320 b8/copy-to-EAX _test-buffered-file/imm32 + 321 05/add-to-EAX 4/imm32 + 322 50/push-EAX + 323 # . . call + 324 e8/call clear-stream/disp32 + 325 # . . discard args + 326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 327 # . clear-stream(_test-error-stream) + 328 # . . push args + 329 68/push _test-error-stream/imm32 + 330 # . . call + 331 e8/call clear-stream/disp32 + 332 # . . discard args + 333 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 334 # . clear-stream(_test-error-buffered-file+4) + 335 # . . push args + 336 b8/copy-to-EAX _test-error-buffered-file/imm32 + 337 05/add-to-EAX 4/imm32 + 338 50/push-EAX + 339 # . . call + 340 e8/call clear-stream/disp32 + 341 # . . discard args + 342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 343 # don't initialize '_test-stream' + 344 # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below + 345 # . var ed/ECX : exit-descriptor + 346 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 347 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 348 # . tailor-exit-descriptor(ed, 12) + 349 # . . push args + 350 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet + 351 51/push-ECX/ed + 352 # . . call + 353 e8/call tailor-exit-descriptor/disp32 + 354 # . . discard args + 355 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 356 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) + 357 # . . push args + 358 51/push-ECX/ed + 359 68/push _test-error-buffered-file/imm32 + 360 68/push _test-buffered-file/imm32 + 361 # . . call + 362 e8/call convert-next-octet/disp32 + 363 # registers except ESP may be clobbered at this point + 364 # pop args to convert-next-octet + 365 # . . discard first 2 args + 366 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 367 # . . restore ed + 368 59/pop-to-ECX + 369 # check that convert-next-octet didn't abort + 370 # . check-ints-equal(ed->value, 0, msg) + 371 # . . push args + 372 68/push "F - test-convert-next-octet: unexpected abort"/imm32 + 373 68/push 0/imm32 + 374 # . . push ed->value + 375 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) + 376 # . . call + 377 e8/call check-ints-equal/disp32 + 378 # . . discard args + 379 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 380 # return if abort + 381 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) + 382 75/jump-if-not-equal $test-convert-next-octet-handles-Eof:end/disp8 + 383 # check-ints-equal(EAX, Eof, msg) + 384 # . . push args + 385 68/push "F - test-convert-next-octet-handles-Eof"/imm32 + 386 68/push 0xffffffff/imm32/Eof + 387 50/push-EAX + 388 # . . call + 389 e8/call check-ints-equal/disp32 + 390 # . . discard args + 391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 392 $test-convert-next-octet-handles-Eof:end: + 393 # . epilog + 394 # don't restore ESP from EBP; manually reclaim locals + 395 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 396 5d/pop-to-EBP + 397 c3/return + 398 + 399 test-convert-next-octet-aborts-on-single-hex-byte: + 400 # - check that a single unaccompanied hex byte aborts + 401 # This test uses exit-descriptors. Use EBP for setting up local variables. + 402 55/push-EBP + 403 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 404 # clear all streams + 405 # . clear-stream(_test-stream) + 406 # . . push args + 407 68/push _test-stream/imm32 + 408 # . . call + 409 e8/call clear-stream/disp32 + 410 # . . discard args + 411 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 412 # . clear-stream(_test-buffered-file+4) + 413 # . . push args + 414 b8/copy-to-EAX _test-buffered-file/imm32 + 415 05/add-to-EAX 4/imm32 + 416 50/push-EAX + 417 # . . call + 418 e8/call clear-stream/disp32 + 419 # . . discard args + 420 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 421 # . clear-stream(_test-error-stream) + 422 # . . push args + 423 68/push _test-error-stream/imm32 + 424 # . . call + 425 e8/call clear-stream/disp32 + 426 # . . discard args + 427 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 428 # . clear-stream(_test-error-buffered-file+4) + 429 # . . push args + 430 b8/copy-to-EAX _test-error-buffered-file/imm32 + 431 05/add-to-EAX 4/imm32 + 432 50/push-EAX + 433 # . . call + 434 e8/call clear-stream/disp32 + 435 # . . discard args + 436 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 437 # initialize '_test-stream' to "a" + 438 # . write(_test-stream, "a") + 439 # . . push args + 440 68/push "a"/imm32 + 441 68/push _test-stream/imm32 + 442 # . . call + 443 e8/call write/disp32 + 444 # . . discard args + 445 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 446 # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below + 447 # . var ed/ECX : exit-descriptor + 448 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 449 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 450 # . tailor-exit-descriptor(ed, 12) + 451 # . . push args + 452 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet + 453 51/push-ECX/ed + 454 # . . call + 455 e8/call tailor-exit-descriptor/disp32 + 456 # . . discard args + 457 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 458 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) + 459 # . . push args + 460 51/push-ECX/ed + 461 68/push _test-error-buffered-file/imm32 + 462 68/push _test-buffered-file/imm32 + 463 # . . call + 464 e8/call convert-next-octet/disp32 + 465 # registers except ESP may be clobbered at this point + 466 # pop args to convert-next-octet + 467 # . . discard first 2 args + 468 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 469 # . . restore ed + 470 59/pop-to-ECX + 471 # check that convert-next-octet aborted + 472 # . check-ints-equal(ed->value, 2, msg) + 473 # . . push args + 474 68/push "F - test-convert-next-octet-aborts-on-single-hex-byte: unexpected abort"/imm32 + 475 68/push 2/imm32 + 476 # . . push ed->value + 477 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) + 478 # . . call + 479 e8/call check-ints-equal/disp32 + 480 # . . discard args + 481 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 482 $test-convert-next-octet-aborts-on-single-hex-byte:end: + 483 # . epilog + 484 # don't restore ESP from EBP; manually reclaim locals + 485 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 486 5d/pop-to-EBP + 487 c3/return + 488 + 489 # read whitespace until a hex byte, and return it + 490 # return Eof if file ends without finding a hex byte + 491 # on '#' skip all bytes until newline + 492 # abort on any other byte + 493 scan-next-byte: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX + 494 # pseudocode: + 495 # while true + 496 # EAX = read-byte-buffered(in) + 497 # if (EAX == Eof) return EAX + 498 # if (is-hex-digit?(EAX)) return EAX + 499 # if (EAX == ' ' or '\t' or '\n') continue + 500 # if (EAX == '#') skip-until-newline(in) + 501 # else error-byte(ed, err, "invalid byte: " EAX) + 502 # + 503 # . prolog + 504 55/push-EBP + 505 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 506 # . save registers + 507 $scan-next-byte:loop: + 508 # EAX = read-byte-buffered(in) + 509 # . . push args + 510 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 511 # . . call + 512 e8/call read-byte-buffered/disp32 + 513 # . . discard args + 514 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 515 # if (EAX == Eof) return EAX + 516 3d/compare-with-EAX 0xffffffff/imm32/Eof + 517 74/jump-if-equal $scan-next-byte:end/disp8 + 518 # if (is-hex-digit?(EAX)) return EAX + 519 # . save EAX for now + 520 50/push-EAX + 521 # . is-hex-digit?(EAX) + 522 # . . push args + 523 50/push-EAX + 524 # . . call + 525 e8/call is-hex-digit?/disp32 + 526 # . . discard args + 527 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 528 # . compare with 'false' + 529 3d/compare-with-EAX 0/imm32 + 530 # . restore EAX (does not affect flags) + 531 58/pop-to-EAX + 532 # . check whether to return + 533 75/jump-if-not-equal $scan-next-byte:end/disp8 + 534 $scan-next-byte:check1: + 535 # if (EAX == ' ') continue + 536 3d/compare-EAX-and 0x20/imm32/space + 537 74/jump-if-equal $scan-next-byte:loop/disp8 + 538 # if (EAX == '\t') continue + 539 3d/compare-EAX-and 9/imm32/tab + 540 74/jump-if-equal $scan-next-byte:loop/disp8 + 541 # if (EAX == '\n') continue + 542 3d/compare-EAX-and 0xa/imm32/newline + 543 74/jump-if-equal $scan-next-byte:loop/disp8 + 544 $scan-next-byte:check2: + 545 # if (EAX == '#') skip-until-newline(in) + 546 3d/compare-with-EAX 0x23/imm32 + 547 75/jump-if-not-equal $scan-next-byte:check3/disp8 + 548 # . skip-until-newline(in) + 549 # . . push args + 550 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 551 # . . call + 552 e8/call skip-until-newline/disp32 + 553 # . . discard args + 554 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 555 eb/jump $scan-next-byte:loop/disp8 + 556 $scan-next-byte:check3: + 557 # otherwise error-byte(ed, err, msg, EAX) + 558 # . . push args + 559 50/push-EAX + 560 68/push "scan-next-byte: invalid byte"/imm32 + 561 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 562 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 563 # . . call + 564 e8/call error-byte/disp32 # never returns + 565 $scan-next-byte:end: + 566 # . restore registers + 567 # . epilog + 568 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 569 5d/pop-to-EBP + 570 c3/return + 571 + 572 test-scan-next-byte: + 573 # - check that the first byte of the input is returned + 574 # This test uses exit-descriptors. Use EBP for setting up local variables. + 575 55/push-EBP + 576 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 577 # clear all streams + 578 # . clear-stream(_test-stream) + 579 # . . push args + 580 68/push _test-stream/imm32 + 581 # . . call + 582 e8/call clear-stream/disp32 + 583 # . . discard args + 584 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 585 # . clear-stream(_test-buffered-file+4) + 586 # . . push args + 587 b8/copy-to-EAX _test-buffered-file/imm32 + 588 05/add-to-EAX 4/imm32 + 589 50/push-EAX + 590 # . . call + 591 e8/call clear-stream/disp32 + 592 # . . discard args + 593 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 594 # . clear-stream(_test-error-stream) + 595 # . . push args + 596 68/push _test-error-stream/imm32 + 597 # . . call + 598 e8/call clear-stream/disp32 + 599 # . . discard args + 600 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 601 # . clear-stream(_test-error-buffered-file+4) + 602 # . . push args + 603 b8/copy-to-EAX _test-error-buffered-file/imm32 + 604 05/add-to-EAX 4/imm32 + 605 50/push-EAX + 606 # . . call + 607 e8/call clear-stream/disp32 + 608 # . . discard args + 609 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 610 # initialize '_test-stream' to "abc" + 611 # . write(_test-stream, "abc") + 612 # . . push args + 613 68/push "abc"/imm32 + 614 68/push _test-stream/imm32 + 615 # . . call + 616 e8/call write/disp32 + 617 # . . discard args + 618 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 619 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + 620 # . var ed/ECX : exit-descriptor + 621 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 622 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 623 # . tailor-exit-descriptor(ed, 12) + 624 # . . push args + 625 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 626 51/push-ECX/ed + 627 # . . call + 628 e8/call tailor-exit-descriptor/disp32 + 629 # . . discard args + 630 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 631 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + 632 # . . push args + 633 51/push-ECX/ed + 634 68/push _test-error-buffered-file/imm32 + 635 68/push _test-buffered-file/imm32 + 636 # . . call + 637 e8/call scan-next-byte/disp32 + 638 # registers except ESP may be clobbered at this point + 639 # pop args to scan-next-byte + 640 # . . discard first 2 args + 641 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 642 # . . restore ed + 643 59/pop-to-ECX + 644 # check that scan-next-byte didn't abort + 645 # . check-ints-equal(ed->value, 0, msg) + 646 # . . push args + 647 68/push "F - test-scan-next-byte: unexpected abort"/imm32 + 648 68/push 0/imm32 + 649 # . . push ed->value + 650 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) + 651 # . . call + 652 e8/call check-ints-equal/disp32 + 653 # . . discard args + 654 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 655 # return if abort + 656 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) + 657 75/jump-if-not-equal $test-scan-next-byte:end/disp8 + 658 # check-ints-equal(EAX, 0x61/a, msg) + 659 # . . push args + 660 68/push "F - test-scan-next-byte"/imm32 + 661 68/push 0x61/imm32/a + 662 50/push-EAX + 663 # . . call + 664 e8/call check-ints-equal/disp32 + 665 # . . discard args + 666 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 667 $test-scan-next-byte:end: + 668 # . epilog + 669 # don't restore ESP from EBP; manually reclaim locals + 670 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 671 5d/pop-to-EBP + 672 c3/return + 673 + 674 test-scan-next-byte-skips-whitespace: + 675 # - check that the first byte after whitespace is returned + 676 # This test uses exit-descriptors. Use EBP for setting up local variables. + 677 55/push-EBP + 678 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 679 # clear all streams + 680 # . clear-stream(_test-stream) + 681 # . . push args + 682 68/push _test-stream/imm32 + 683 # . . call + 684 e8/call clear-stream/disp32 + 685 # . . discard args + 686 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 687 # . clear-stream(_test-buffered-file+4) + 688 # . . push args + 689 b8/copy-to-EAX _test-buffered-file/imm32 + 690 05/add-to-EAX 4/imm32 + 691 50/push-EAX + 692 # . . call + 693 e8/call clear-stream/disp32 + 694 # . . discard args + 695 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 696 # . clear-stream(_test-error-stream) + 697 # . . push args + 698 68/push _test-error-stream/imm32 + 699 # . . call + 700 e8/call clear-stream/disp32 + 701 # . . discard args + 702 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 703 # . clear-stream(_test-error-buffered-file+4) + 704 # . . push args + 705 b8/copy-to-EAX _test-error-buffered-file/imm32 + 706 05/add-to-EAX 4/imm32 + 707 50/push-EAX + 708 # . . call + 709 e8/call clear-stream/disp32 + 710 # . . discard args + 711 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 712 # initialize '_test-stream' to input with leading whitespace + 713 # . write(_test-stream, text) + 714 # . . push args + 715 68/push " abc"/imm32 + 716 68/push _test-stream/imm32 + 717 # . . call + 718 e8/call write/disp32 + 719 # . . discard args + 720 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 721 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + 722 # . var ed/ECX : exit-descriptor + 723 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 724 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 725 # . tailor-exit-descriptor(ed, 12) + 726 # . . push args + 727 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 728 51/push-ECX/ed + 729 # . . call + 730 e8/call tailor-exit-descriptor/disp32 + 731 # . . discard args + 732 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 733 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + 734 # . . push args + 735 51/push-ECX/ed + 736 68/push _test-error-buffered-file/imm32 + 737 68/push _test-buffered-file/imm32 + 738 # . . call + 739 e8/call scan-next-byte/disp32 + 740 # registers except ESP may be clobbered at this point + 741 # pop args to scan-next-byte + 742 # . . discard first 2 args + 743 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 744 # . . restore ed + 745 59/pop-to-ECX + 746 # check that scan-next-byte didn't abort + 747 # . check-ints-equal(ed->value, 0, msg) + 748 # . . push args + 749 68/push "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32 + 750 68/push 0/imm32 + 751 # . . push ed->value + 752 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) + 753 # . . call + 754 e8/call check-ints-equal/disp32 + 755 # . . discard args + 756 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 757 # return if abort + 758 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) + 759 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace:end/disp8 + 760 # check-ints-equal(EAX, 0x61/a, msg) + 761 # . . push args + 762 68/push "F - test-scan-next-byte-skips-whitespace"/imm32 + 763 68/push 0x61/imm32/a + 764 50/push-EAX + 765 # . . call + 766 e8/call check-ints-equal/disp32 + 767 # . . discard args + 768 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 769 $test-scan-next-byte-skips-whitespace:end: + 770 # . epilog + 771 # don't restore ESP from EBP; manually reclaim locals + 772 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 773 5d/pop-to-EBP + 774 c3/return + 775 + 776 test-scan-next-byte-skips-comment: + 777 # - check that the first byte after a comment (and newline) is returned + 778 # This test uses exit-descriptors. Use EBP for setting up local variables. + 779 55/push-EBP + 780 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 781 # clear all streams + 782 # . clear-stream(_test-stream) + 783 # . . push args + 784 68/push _test-stream/imm32 + 785 # . . call + 786 e8/call clear-stream/disp32 + 787 # . . discard args + 788 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 789 # . clear-stream(_test-buffered-file+4) + 790 # . . push args + 791 b8/copy-to-EAX _test-buffered-file/imm32 + 792 05/add-to-EAX 4/imm32 + 793 50/push-EAX + 794 # . . call + 795 e8/call clear-stream/disp32 + 796 # . . discard args + 797 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 798 # . clear-stream(_test-error-stream) + 799 # . . push args + 800 68/push _test-error-stream/imm32 + 801 # . . call + 802 e8/call clear-stream/disp32 + 803 # . . discard args + 804 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 805 # . clear-stream(_test-error-buffered-file+4) + 806 # . . push args + 807 b8/copy-to-EAX _test-error-buffered-file/imm32 + 808 05/add-to-EAX 4/imm32 + 809 50/push-EAX + 810 # . . call + 811 e8/call clear-stream/disp32 + 812 # . . discard args + 813 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 814 # initialize '_test-stream' to input with leading comment + 815 # . write(_test-stream, comment) + 816 # . . push args + 817 68/push "#x\n"/imm32 + 818 68/push _test-stream/imm32 + 819 # . . call + 820 e8/call write/disp32 + 821 # . . discard args + 822 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 823 # . write(_test-stream, real text) + 824 # . . push args + 825 68/push "ab"/imm32 + 826 68/push _test-stream/imm32 + 827 # . . call + 828 e8/call write/disp32 + 829 # . . discard args + 830 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 831 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + 832 # . var ed/ECX : exit-descriptor + 833 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 834 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 835 # . tailor-exit-descriptor(ed, 12) + 836 # . . push args + 837 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 838 51/push-ECX/ed + 839 # . . call + 840 e8/call tailor-exit-descriptor/disp32 + 841 # . . discard args + 842 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 843 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + 844 # . . push args + 845 51/push-ECX/ed + 846 68/push _test-error-buffered-file/imm32 + 847 68/push _test-buffered-file/imm32 + 848 # . . call + 849 e8/call scan-next-byte/disp32 + 850 # registers except ESP may be clobbered at this point + 851 # pop args to scan-next-byte + 852 # . . discard first 2 args + 853 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 854 # . . restore ed + 855 59/pop-to-ECX + 856 # check that scan-next-byte didn't abort + 857 # . check-ints-equal(ed->value, 0, msg) + 858 # . . push args + 859 68/push "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32 + 860 68/push 0/imm32 + 861 # . . push ed->value + 862 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) + 863 # . . call + 864 e8/call check-ints-equal/disp32 + 865 # . . discard args + 866 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 867 # return if abort + 868 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) + 869 75/jump-if-not-equal $test-scan-next-byte-skips-comment:end/disp8 + 870 # check-ints-equal(EAX, 0x61/a, msg) + 871 # . . push args + 872 68/push "F - test-scan-next-byte-skips-comment"/imm32 + 873 68/push 0x61/imm32/a + 874 50/push-EAX + 875 # . . call + 876 e8/call check-ints-equal/disp32 + 877 # . . discard args + 878 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 879 $test-scan-next-byte-skips-comment:end: + 880 # . epilog + 881 # don't restore ESP from EBP; manually reclaim locals + 882 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 883 5d/pop-to-EBP + 884 c3/return + 885 + 886 test-scan-next-byte-skips-comment-and-whitespace: + 887 # - check that the first byte after a comment and any further whitespace is returned + 888 # This test uses exit-descriptors. Use EBP for setting up local variables. + 889 55/push-EBP + 890 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 891 # clear all streams + 892 # . clear-stream(_test-stream) + 893 # . . push args + 894 68/push _test-stream/imm32 + 895 # . . call + 896 e8/call clear-stream/disp32 + 897 # . . discard args + 898 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 899 # . clear-stream(_test-buffered-file+4) + 900 # . . push args + 901 b8/copy-to-EAX _test-buffered-file/imm32 + 902 05/add-to-EAX 4/imm32 + 903 50/push-EAX + 904 # . . call + 905 e8/call clear-stream/disp32 + 906 # . . discard args + 907 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 908 # . clear-stream(_test-error-stream) + 909 # . . push args + 910 68/push _test-error-stream/imm32 + 911 # . . call + 912 e8/call clear-stream/disp32 + 913 # . . discard args + 914 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 915 # . clear-stream(_test-error-buffered-file+4) + 916 # . . push args + 917 b8/copy-to-EAX _test-error-buffered-file/imm32 + 918 05/add-to-EAX 4/imm32 + 919 50/push-EAX + 920 # . . call + 921 e8/call clear-stream/disp32 + 922 # . . discard args + 923 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 924 # initialize '_test-stream' to input with leading comment and more whitespace after newline + 925 # . write(_test-stream, comment) + 926 # . . push args + 927 68/push "#x\n"/imm32 + 928 68/push _test-stream/imm32 + 929 # . . call + 930 e8/call write/disp32 + 931 # . . discard args + 932 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 933 # . write(_test-stream, real text) + 934 # . . push args + 935 68/push " ab"/imm32 + 936 68/push _test-stream/imm32 + 937 # . . call + 938 e8/call write/disp32 + 939 # . . discard args + 940 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 941 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + 942 # . var ed/ECX : exit-descriptor + 943 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 944 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 945 # . tailor-exit-descriptor(ed, 12) + 946 # . . push args + 947 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 948 51/push-ECX/ed + 949 # . . call + 950 e8/call tailor-exit-descriptor/disp32 + 951 # . . discard args + 952 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 953 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + 954 # . . push args + 955 51/push-ECX/ed + 956 68/push _test-error-buffered-file/imm32 + 957 68/push _test-buffered-file/imm32 + 958 # . . call + 959 e8/call scan-next-byte/disp32 + 960 # registers except ESP may be clobbered at this point + 961 # pop args to scan-next-byte + 962 # . . discard first 2 args + 963 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 964 # . . restore ed + 965 59/pop-to-ECX + 966 # check that scan-next-byte didn't abort + 967 # . check-ints-equal(ed->value, 0, msg) + 968 # . . push args + 969 68/push "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32 + 970 68/push 0/imm32 + 971 # . . push ed->value + 972 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) + 973 # . . call + 974 e8/call check-ints-equal/disp32 + 975 # . . discard args + 976 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 977 # return if abort + 978 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) + 979 75/jump-if-not-equal $test-scan-next-byte-skips-comment-and-whitespace:end/disp8 + 980 # check-ints-equal(EAX, 0x61/a, msg) + 981 # . . push args + 982 68/push "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32 + 983 68/push 0x61/imm32/a + 984 50/push-EAX + 985 # . . call + 986 e8/call check-ints-equal/disp32 + 987 # . . discard args + 988 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 989 $test-scan-next-byte-skips-comment-and-whitespace:end: + 990 # . epilog + 991 # don't restore ESP from EBP; manually reclaim locals + 992 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 993 5d/pop-to-EBP + 994 c3/return + 995 + 996 test-scan-next-byte-skips-whitespace-and-comment: + 997 # - check that the first byte after any whitespace and comments is returned + 998 # This test uses exit-descriptors. Use EBP for setting up local variables. + 999 55/push-EBP +1000 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1001 # clear all streams +1002 # . clear-stream(_test-stream) +1003 # . . push args +1004 68/push _test-stream/imm32 +1005 # . . call +1006 e8/call clear-stream/disp32 +1007 # . . discard args +1008 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1009 # . clear-stream(_test-buffered-file+4) +1010 # . . push args +1011 b8/copy-to-EAX _test-buffered-file/imm32 +1012 05/add-to-EAX 4/imm32 +1013 50/push-EAX +1014 # . . call +1015 e8/call clear-stream/disp32 +1016 # . . discard args +1017 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1018 # . clear-stream(_test-error-stream) +1019 # . . push args +1020 68/push _test-error-stream/imm32 +1021 # . . call +1022 e8/call clear-stream/disp32 +1023 # . . discard args +1024 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1025 # . clear-stream(_test-error-buffered-file+4) +1026 # . . push args +1027 b8/copy-to-EAX _test-error-buffered-file/imm32 +1028 05/add-to-EAX 4/imm32 +1029 50/push-EAX +1030 # . . call +1031 e8/call clear-stream/disp32 +1032 # . . discard args +1033 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1034 # initialize '_test-stream' to input with leading whitespace and comment +1035 # . write(_test-stream, comment) +1036 # . . push args +1037 68/push " #x\n"/imm32 +1038 68/push _test-stream/imm32 +1039 # . . call +1040 e8/call write/disp32 +1041 # . . discard args +1042 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1043 # . write(_test-stream, real text) +1044 # . . push args +1045 68/push "ab"/imm32 +1046 68/push _test-stream/imm32 +1047 # . . call +1048 e8/call write/disp32 +1049 # . . discard args +1050 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1051 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below +1052 # . var ed/ECX : exit-descriptor +1053 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +1054 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1055 # . tailor-exit-descriptor(ed, 12) +1056 # . . push args +1057 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte +1058 51/push-ECX/ed +1059 # . . call +1060 e8/call tailor-exit-descriptor/disp32 +1061 # . . discard args +1062 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1063 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) +1064 # . . push args +1065 51/push-ECX/ed +1066 68/push _test-error-buffered-file/imm32 +1067 68/push _test-buffered-file/imm32 +1068 # . . call +1069 e8/call scan-next-byte/disp32 +1070 # registers except ESP may be clobbered at this point +1071 # pop args to scan-next-byte +1072 # . . discard first 2 args +1073 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1074 # . . restore ed +1075 59/pop-to-ECX +1076 # check that scan-next-byte didn't abort +1077 # . check-ints-equal(ed->value, 0, msg) +1078 # . . push args +1079 68/push "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32 +1080 68/push 0/imm32 +1081 # . . push ed->value +1082 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) +1083 # . . call +1084 e8/call check-ints-equal/disp32 +1085 # . . discard args +1086 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1087 # return if abort +1088 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) +1089 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace-and-comment:end/disp8 +1090 # check-ints-equal(EAX, 0x61/a, msg) +1091 # . . push args +1092 68/push "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32 +1093 68/push 0x61/imm32/a +1094 50/push-EAX +1095 # . . call +1096 e8/call check-ints-equal/disp32 +1097 # . . discard args +1098 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1099 $test-scan-next-byte-skips-whitespace-and-comment:end: +1100 # . epilog +1101 # don't restore ESP from EBP; manually reclaim locals +1102 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1103 5d/pop-to-EBP +1104 c3/return +1105 +1106 test-scan-next-byte-reads-final-byte: +1107 # - check that the final byte in input is returned +1108 # This test uses exit-descriptors. Use EBP for setting up local variables. +1109 55/push-EBP +1110 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1111 # clear all streams +1112 # . clear-stream(_test-stream) +1113 # . . push args +1114 68/push _test-stream/imm32 +1115 # . . call +1116 e8/call clear-stream/disp32 +1117 # . . discard args +1118 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1119 # . clear-stream(_test-buffered-file+4) +1120 # . . push args +1121 b8/copy-to-EAX _test-buffered-file/imm32 +1122 05/add-to-EAX 4/imm32 +1123 50/push-EAX +1124 # . . call +1125 e8/call clear-stream/disp32 +1126 # . . discard args +1127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1128 # . clear-stream(_test-error-stream) +1129 # . . push args +1130 68/push _test-error-stream/imm32 +1131 # . . call +1132 e8/call clear-stream/disp32 +1133 # . . discard args +1134 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1135 # . clear-stream(_test-error-buffered-file+4) +1136 # . . push args +1137 b8/copy-to-EAX _test-error-buffered-file/imm32 +1138 05/add-to-EAX 4/imm32 +1139 50/push-EAX +1140 # . . call +1141 e8/call clear-stream/disp32 +1142 # . . discard args +1143 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1144 # initialize '_test-stream' to input with single character +1145 # . write(_test-stream, character) +1146 # . . push args +1147 68/push "a"/imm32 +1148 68/push _test-stream/imm32 +1149 # . . call +1150 e8/call write/disp32 +1151 # . . discard args +1152 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1153 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below +1154 # . var ed/ECX : exit-descriptor +1155 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +1156 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1157 # . tailor-exit-descriptor(ed, 12) +1158 # . . push args +1159 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte +1160 51/push-ECX/ed +1161 # . . call +1162 e8/call tailor-exit-descriptor/disp32 +1163 # . . discard args +1164 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1165 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) +1166 # . . push args +1167 51/push-ECX/ed +1168 68/push _test-error-buffered-file/imm32 +1169 68/push _test-buffered-file/imm32 +1170 # . . call +1171 e8/call scan-next-byte/disp32 +1172 # registers except ESP may be clobbered at this point +1173 # pop args to scan-next-byte +1174 # . . discard first 2 args +1175 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1176 # . . restore ed +1177 59/pop-to-ECX +1178 # check that scan-next-byte didn't abort +1179 # . check-ints-equal(ed->value, 0, msg) +1180 # . . push args +1181 68/push "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32 +1182 68/push 0/imm32 +1183 # . . push ed->value +1184 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) +1185 # . . call +1186 e8/call check-ints-equal/disp32 +1187 # . . discard args +1188 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1189 # return if abort +1190 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) +1191 75/jump-if-not-equal $test-scan-next-byte-reads-final-byte:end/disp8 +1192 # check-ints-equal(EAX, 0x61/a, msg) +1193 # . . push args +1194 68/push "F - test-scan-next-byte-reads-final-byte"/imm32 +1195 68/push 0x61/imm32/a +1196 50/push-EAX +1197 # . . call +1198 e8/call check-ints-equal/disp32 +1199 # . . discard args +1200 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1201 $test-scan-next-byte-reads-final-byte:end: +1202 # . epilog +1203 # don't restore ESP from EBP; manually reclaim locals +1204 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1205 5d/pop-to-EBP +1206 c3/return +1207 +1208 test-scan-next-byte-handles-Eof: +1209 # - check that the right sentinel value is returned when there's no data remaining to be read +1210 # This test uses exit-descriptors. Use EBP for setting up local variables. +1211 55/push-EBP +1212 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1213 # clear all streams +1214 # . clear-stream(_test-stream) +1215 # . . push args +1216 68/push _test-stream/imm32 +1217 # . . call +1218 e8/call clear-stream/disp32 +1219 # . . discard args +1220 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1221 # . clear-stream(_test-buffered-file+4) +1222 # . . push args +1223 b8/copy-to-EAX _test-buffered-file/imm32 +1224 05/add-to-EAX 4/imm32 +1225 50/push-EAX +1226 # . . call +1227 e8/call clear-stream/disp32 +1228 # . . discard args +1229 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1230 # . clear-stream(_test-error-stream) +1231 # . . push args +1232 68/push _test-error-stream/imm32 +1233 # . . call +1234 e8/call clear-stream/disp32 +1235 # . . discard args +1236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1237 # . clear-stream(_test-error-buffered-file+4) +1238 # . . push args +1239 b8/copy-to-EAX _test-error-buffered-file/imm32 +1240 05/add-to-EAX 4/imm32 +1241 50/push-EAX +1242 # . . call +1243 e8/call clear-stream/disp32 +1244 # . . discard args +1245 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1246 # leave '_test-stream' empty +1247 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below +1248 # . var ed/ECX : exit-descriptor +1249 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +1250 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1251 # . tailor-exit-descriptor(ed, 12) +1252 # . . push args +1253 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte +1254 51/push-ECX/ed +1255 # . . call +1256 e8/call tailor-exit-descriptor/disp32 +1257 # . . discard args +1258 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1259 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) +1260 # . . push args +1261 51/push-ECX/ed +1262 68/push _test-error-buffered-file/imm32 +1263 68/push _test-buffered-file/imm32 +1264 # . . call +1265 e8/call scan-next-byte/disp32 +1266 # registers except ESP may be clobbered at this point +1267 # pop args to scan-next-byte +1268 # . . discard first 2 args +1269 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1270 # . . restore ed +1271 59/pop-to-ECX +1272 # check that scan-next-byte didn't abort +1273 # . check-ints-equal(ed->value, 0, msg) +1274 # . . push args +1275 68/push "F - test-scan-next-byte-handles-Eof: unexpected abort"/imm32 +1276 68/push 0/imm32 +1277 # . . push ed->value +1278 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) +1279 # . . call +1280 e8/call check-ints-equal/disp32 +1281 # . . discard args +1282 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1283 # return if abort +1284 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) +1285 75/jump-if-not-equal $test-scan-next-byte-handles-Eof:end/disp8 +1286 # check-ints-equal(EAX, Eof, msg) +1287 # . . push args +1288 68/push "F - test-scan-next-byte-handles-Eof"/imm32 +1289 68/push 0xffffffff/imm32/Eof +1290 50/push-EAX +1291 # . . call +1292 e8/call check-ints-equal/disp32 +1293 # . . discard args +1294 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1295 $test-scan-next-byte-handles-Eof:end: +1296 # . epilog +1297 # don't restore ESP from EBP; manually reclaim locals +1298 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1299 5d/pop-to-EBP +1300 c3/return +1301 +1302 test-scan-next-byte-aborts-on-invalid-byte: +1303 # - check that the a bad byte immediately aborts +1304 # This test uses exit-descriptors. Use EBP for setting up local variables. +1305 55/push-EBP +1306 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1307 # clear all streams +1308 # . clear-stream(_test-stream) +1309 # . . push args +1310 68/push _test-stream/imm32 +1311 # . . call +1312 e8/call clear-stream/disp32 +1313 # . . discard args +1314 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1315 # . clear-stream(_test-buffered-file+4) +1316 # . . push args +1317 b8/copy-to-EAX _test-buffered-file/imm32 +1318 05/add-to-EAX 4/imm32 +1319 50/push-EAX +1320 # . . call +1321 e8/call clear-stream/disp32 +1322 # . . discard args +1323 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1324 # . clear-stream(_test-error-stream) +1325 # . . push args +1326 68/push _test-error-stream/imm32 +1327 # . . call +1328 e8/call clear-stream/disp32 +1329 # . . discard args +1330 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1331 # . clear-stream(_test-error-buffered-file+4) +1332 # . . push args +1333 b8/copy-to-EAX _test-error-buffered-file/imm32 +1334 05/add-to-EAX 4/imm32 +1335 50/push-EAX +1336 # . . call +1337 e8/call clear-stream/disp32 +1338 # . . discard args +1339 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1340 # initialize '_test-stream' to "x" +1341 # . write(_test-stream, "x") +1342 # . . push args +1343 68/push "x"/imm32 +1344 68/push _test-stream/imm32 +1345 # . . call +1346 e8/call write/disp32 +1347 # . . discard args +1348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1349 # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below +1350 # . var ed/ECX : exit-descriptor +1351 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +1352 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1353 # . tailor-exit-descriptor(ed, 12) +1354 # . . push args +1355 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte +1356 51/push-ECX/ed +1357 # . . call +1358 e8/call tailor-exit-descriptor/disp32 +1359 # . . discard args +1360 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1361 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) +1362 # . . push args +1363 51/push-ECX/ed +1364 68/push _test-error-buffered-file/imm32 +1365 68/push _test-buffered-file/imm32 +1366 # . . call +1367 e8/call scan-next-byte/disp32 +1368 # registers except ESP may be clobbered at this point +1369 # pop args to scan-next-byte +1370 # . . discard first 2 args +1371 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1372 # . . restore ed +1373 59/pop-to-ECX +1374 # check that scan-next-byte aborted +1375 # . check-ints-equal(ed->value, 2, msg) +1376 # . . push args +1377 68/push "F - test-scan-next-byte-aborts-on-invalid-byte"/imm32 +1378 68/push 2/imm32 +1379 # . . push ed->value +1380 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) +1381 # . . call +1382 e8/call check-ints-equal/disp32 +1383 # . . discard args +1384 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1385 $test-scan-next-byte-aborts-on-invalid-byte:end: +1386 # . epilog +1387 # don't restore ESP from EBP; manually reclaim locals +1388 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1389 5d/pop-to-EBP +1390 c3/return +1391 +1392 skip-until-newline: # in : (address buffered-file) -> <void> +1393 # pseudocode: +1394 # push EAX +1395 # while true +1396 # EAX = read-byte-buffered(in) +1397 # if (EAX == Eof) break +1398 # if (EAX == 0x0a) break +1399 # pop EAX +1400 # . prolog +1401 55/push-EBP +1402 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1403 # . save registers +1404 50/push-EAX +1405 $skip-until-newline:loop: +1406 # . EAX = read-byte-buffered(in) +1407 # . . push args +1408 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1409 # . . call +1410 e8/call read-byte-buffered/disp32 +1411 # . . discard args +1412 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1413 # . if (EAX == Eof) break +1414 3d/compare-EAX-and 0xffffffff/imm32/Eof +1415 74/jump-if-equal $skip-until-newline:end/disp8 +1416 # . if (EAX != 0xa/newline) loop +1417 3d/compare-EAX-and 0xa/imm32/newline +1418 75/jump-if-not-equal $skip-until-newline:loop/disp8 +1419 $skip-until-newline:end: +1420 # . restore registers +1421 58/pop-to-EAX +1422 # . epilog +1423 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1424 5d/pop-to-EBP +1425 c3/return +1426 +1427 test-skip-until-newline: +1428 # - check that the read pointer points after the newline +1429 # setup +1430 # . clear-stream(_test-stream) +1431 # . . push args +1432 68/push _test-stream/imm32 +1433 # . . call +1434 e8/call clear-stream/disp32 +1435 # . . discard args +1436 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1437 # . clear-stream(_test-buffered-file+4) +1438 # . . push args +1439 b8/copy-to-EAX _test-buffered-file/imm32 +1440 05/add-to-EAX 4/imm32 +1441 50/push-EAX +1442 # . . call +1443 e8/call clear-stream/disp32 +1444 # . . discard args +1445 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1446 # initialize '_test-stream' to "abc\nde" +1447 # . write(_test-stream, "abc") +1448 # . . push args +1449 68/push "abc\n"/imm32 +1450 68/push _test-stream/imm32 +1451 # . . call +1452 e8/call write/disp32 +1453 # . . discard args +1454 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1455 # . write(_test-stream, "de") +1456 # . . push args +1457 68/push "de"/imm32 +1458 68/push _test-stream/imm32 +1459 # . . call +1460 e8/call write/disp32 +1461 # . . discard args +1462 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1463 # skip-until-newline(_test-buffered-file) +1464 # . . push args +1465 68/push _test-buffered-file/imm32 +1466 # . . call +1467 e8/call skip-until-newline/disp32 +1468 # . . discard args +1469 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1470 # check-ints-equal(_test-buffered-file->read, 4, msg) +1471 # . . push args +1472 68/push "F - test-skip-until-newline"/imm32 +1473 68/push 4/imm32 +1474 b8/copy-to-EAX _test-buffered-file/imm32 +1475 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8) +1476 # . . call +1477 e8/call check-ints-equal/disp32 +1478 # . . discard args +1479 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1480 # . end +1481 c3/return +1482 +1483 == data +1484 +1485 _test-error-stream: +1486 # current write index +1487 0/imm32 +1488 # current read index +1489 0/imm32 +1490 # line +1491 0x80/imm32 # 128 bytes +1492 # data (8 lines x 16 bytes/line) 1493 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1494 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1495 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1496 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -1497 -1498 # a test buffered file for _test-error-stream -1499 _test-error-buffered-file: -1500 # file descriptor or (address stream) -1501 _test-error-stream/imm32 -1502 # current write index -1503 0/imm32 -1504 # current read index -1505 0/imm32 -1506 # length -1507 6/imm32 -1508 # data -1509 00 00 00 00 00 00 # 6 bytes -1510 -1511 # . . vim:nowrap:textwidth=0 +1497 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +1498 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +1499 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +1500 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +1501 +1502 # a test buffered file for _test-error-stream +1503 _test-error-buffered-file: +1504 # file descriptor or (address stream) +1505 _test-error-stream/imm32 +1506 # current write index +1507 0/imm32 +1508 # current read index +1509 0/imm32 +1510 # length +1511 6/imm32 +1512 # data +1513 00 00 00 00 00 00 # 6 bytes +1514 +1515 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/pack.subx.html b/html/subx/apps/pack.subx.html index 9a8758f4..3e66fd64 100644 --- a/html/subx/apps/pack.subx.html +++ b/html/subx/apps/pack.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/pack.subx - - + + @@ -14,19 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } -.subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } -.subxS1Comment { color: #0000af; } -.SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.Folded { color: #080808; background-color: #949494; } -.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } -.subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } .subxH1Comment { color: #005faf; text-decoration: underline; } +.subxComment { color: #005faf; } +.subxS1Comment { color: #0000af; } +.LineNr { } +.subxTest { color: #5f8700; } +.SpecialChar { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Folded { color: #080808; background-color: #949494; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxS2Comment { color: #8a8a8a; } --> @@ -43,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -83,6121 +81,4675 @@ if ('onhashchange' in window) { 19 # . 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 20 21 Entry: # run tests if necessary, convert stdin if not - 22 - 23 # for debugging: run a single test - 24 #? e8/call test-emit-non-number-with-all-hex-digits-and-metadata/disp32 - 25 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 26 #? eb/jump $main:end/disp8 - 27 - 28 # . prolog - 29 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 30 # - if argc > 1 and argv[1] == "test", then return run_tests() - 31 # . argc > 1 - 32 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 33 7e/jump-if-lesser-or-equal $run-main/disp8 - 34 # . argv[1] == "test" - 35 # . . push args - 36 68/push "test"/imm32 - 37 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 38 # . . call - 39 e8/call kernel-string-equal?/disp32 - 40 # . . discard args - 41 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 42 # . check result - 43 3d/compare-EAX-and 1/imm32 - 44 75/jump-if-not-equal $run-main/disp8 - 45 # . run-tests() - 46 e8/call run-tests/disp32 - 47 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 48 eb/jump $main:end/disp8 - 49 $run-main: - 50 # - otherwise convert stdin - 51 # var ed/EAX : exit-descriptor - 52 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 53 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - 54 # configure ed to really exit() - 55 # . ed->target = 0 - 56 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 57 # return convert(Stdin, 1/stdout, 2/stderr, ed) - 58 # . . push args - 59 50/push-EAX/ed - 60 68/push Stderr/imm32 - 61 68/push Stdout/imm32 - 62 68/push Stdin/imm32 - 63 # . . call - 64 e8/call convert/disp32 - 65 # . . discard args - 66 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 67 # . syscall(exit, 0) - 68 bb/copy-to-EBX 0/imm32 - 69 $main:end: - 70 b8/copy-to-EAX 1/imm32/exit - 71 cd/syscall 0x80/imm8 - 72 - 73 # - big picture - 74 # We'll operate on each line/instruction in isolation. That way we only need to - 75 # allocate memory for converting a single instruction. - 76 # - 77 # To pack an entire file, convert every segment in it - 78 # To convert a code segment, convert every instruction (line) until the next segment header - 79 # To convert a non-data segment, convert every word until the next segment header + 22 # initialize heap + 23 # . Heap = new-segment(64KB) + 24 # . . push args + 25 68/push Heap/imm32 + 26 68/push 0x10000/imm32/64KB + 27 # . . call + 28 e8/call new-segment/disp32 + 29 # . . discard args + 30 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 31 + 32 # . prolog + 33 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 34 # - if argc > 1 and argv[1] == "test", then return run_tests() + 35 # . argc > 1 + 36 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP + 37 7e/jump-if-lesser-or-equal $run-main/disp8 + 38 # . argv[1] == "test" + 39 # . . push args + 40 68/push "test"/imm32 + 41 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 42 # . . call + 43 e8/call kernel-string-equal?/disp32 + 44 # . . discard args + 45 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 46 # . check result + 47 3d/compare-EAX-and 1/imm32 + 48 75/jump-if-not-equal $run-main/disp8 + 49 # . run-tests() + 50 e8/call run-tests/disp32 + 51 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 52 eb/jump $main:end/disp8 + 53 $run-main: + 54 # - otherwise convert stdin + 55 # var ed/EAX : exit-descriptor + 56 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 57 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX + 58 # configure ed to really exit() + 59 # . ed->target = 0 + 60 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 61 # return convert(Stdin, 1/stdout, 2/stderr, ed) + 62 # . . push args + 63 50/push-EAX/ed + 64 68/push Stderr/imm32 + 65 68/push Stdout/imm32 + 66 68/push Stdin/imm32 + 67 # . . call + 68 e8/call convert/disp32 + 69 # . . discard args + 70 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 71 # . syscall(exit, 0) + 72 bb/copy-to-EBX 0/imm32 + 73 $main:end: + 74 b8/copy-to-EAX 1/imm32/exit + 75 cd/syscall 0x80/imm8 + 76 + 77 # - big picture + 78 # We'll operate on each line/instruction in isolation. That way we only need to + 79 # allocate memory for converting a single instruction. 80 # - 81 # primary state: line - 82 # stream of 512 bytes; abort if it ever overflows - 83 - 84 # conceptual hierarchy within a line: - 85 # line = words separated by ' ', maybe followed by comment starting with '#' - 86 # word = datum until '/', then 0 or more metadata separated by '/' - 87 # - 88 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives: - 89 # next-token(stream, delim char) -> slice (start, end pointers) - 90 # next-token-from-slice(start, end, delim char) -> slice - 91 # slice-equal?(slice, string) - 92 - 93 convert: # in : (address buffered-file), out : (address buffered-file) -> <void> - 94 # pseudocode: - 95 # var line = new-stream(512, 1) - 96 # var in-code? = false - 97 # while true - 98 # clear-stream(line) - 99 # read-line-buffered(in, line) - 100 # if (line->write == 0) break # end of file - 101 # var word-slice = next-word(line) - 102 # if slice-empty?(word-slice) # whitespace - 103 # write-stream-data(out, line) - 104 # else if (slice-equal?(word-slice, "==")) - 105 # word-slice = next-word(line) - 106 # in-code? = slice-equal?(word-slice, "code") + 81 # To pack an entire file, convert every segment in it + 82 # To convert a code segment, convert every instruction (line) until the next segment header + 83 # To convert a non-data segment, convert every word until the next segment header + 84 # + 85 # primary state: line + 86 # stream of 512 bytes; abort if it ever overflows + 87 + 88 # conceptual hierarchy within a line: + 89 # line = words separated by ' ', maybe followed by comment starting with '#' + 90 # word = datum until '/', then 0 or more metadata separated by '/' + 91 # + 92 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives: + 93 # next-token(stream, delim char) -> slice (start, end pointers) + 94 # next-token-from-slice(start, end, delim char) -> slice + 95 # slice-equal?(slice, string) + 96 + 97 convert: # in : (address buffered-file), out : (address buffered-file) -> <void> + 98 # pseudocode: + 99 # var line = new-stream(512, 1) + 100 # var in-code? = false + 101 # while true + 102 # clear-stream(line) + 103 # read-line-buffered(in, line) + 104 # if (line->write == 0) break # end of file + 105 # var word-slice = next-word(line) + 106 # if slice-empty?(word-slice) # whitespace 107 # write-stream-data(out, line) - 108 # else if (in-code?) - 109 # rewind-stream(line) - 110 # convert-instruction(line, out) - 111 # else - 112 # rewind-stream(line) - 113 # convert-data(line, out) - 114 # flush(out) - 115 # - 116 # . prolog - 117 55/push-EBP - 118 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 119 # . save registers - 120 50/push-EAX - 121 51/push-ECX - 122 52/push-EDX - 123 53/push-EBX - 124 # var line/ECX : (address stream byte) = stream(512) - 125 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP - 126 68/push 0x200/imm32/length - 127 68/push 0/imm32/read - 128 68/push 0/imm32/write - 129 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 130 # var word-slice/EDX = {0, 0} - 131 68/push 0/imm32/end - 132 68/push 0/imm32/curr - 133 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - 134 # var in-code?/EBX = false - 135 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 136 $convert:loop: - 137 # clear-stream(line) - 138 # . . push args - 139 51/push-ECX - 140 # . . call - 141 e8/call clear-stream/disp32 - 142 # . . discard args - 143 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 144 # read-line-buffered(in, line) - 145 # . . push args - 146 51/push-ECX - 147 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 148 # . . call - 149 e8/call read-line-buffered/disp32 - 150 # . . discard args - 151 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 152 $convert:check0: - 153 # if (line->write == 0) break - 154 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX - 155 0f 84/jump-if-equal $convert:break/disp32 - 156 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- - 182 # next-word(line, word-slice) - 183 # . . push args - 184 52/push-EDX - 185 51/push-ECX - 186 # . . call - 187 e8/call next-word/disp32 - 188 # . . discard args - 189 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 190 $convert:check1: - 191 # if (slice-empty?(word-slice)) write-stream-data(out, line) - 192 # . EAX = slice-empty?(word-slice) - 193 # . . push args - 194 52/push-EDX - 195 # . . call - 196 e8/call slice-empty?/disp32 - 197 # . . discard args - 198 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 199 # . if (EAX != 0) write-stream-data(out, line) - 200 3d/compare-EAX-and 0/imm32 - 201 0f 85/jump-if-not-equal $convert:pass-through/disp32 - 202 $convert:check2: - 203 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- - 245 # if (slice-equal?(word-slice, "==")) - 246 # word-slice = next-word(line) - 247 # in-code? = slice-equal?(word-slice, "code") - 248 # write-stream-data(out, line) - 249 # . EAX = slice-equal?(word-slice, "==") - 250 # . . push args - 251 68/push "=="/imm32 - 252 52/push-EDX - 253 # . . call - 254 e8/call slice-equal?/disp32 - 255 # . . discard args - 256 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 257 # . if (EAX == 0) goto check3 - 258 3d/compare-EAX-and 0/imm32 - 259 0f 84/jump-if-equal $convert:check3/disp32 - 260 # . next-word(line, word-slice) - 261 # . . push args - 262 52/push-EDX - 263 51/push-ECX - 264 # . . call - 265 e8/call next-word/disp32 - 266 # . . discard args - 267 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 268 +-- 42 lines: #? # dump segment name --------------------------------------------------------------------------------------------------------------------- - 310 # . in-code? = slice-equal?(word-slice, "code") - 311 # . . push args - 312 68/push "code"/imm32 - 313 52/push-EDX - 314 # . . call - 315 e8/call slice-equal?/disp32 - 316 # . . discard args - 317 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 318 # . . in-code? = EAX - 319 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - 320 # . goto pass-through - 321 eb/jump $convert:pass-through/disp8 - 322 $convert:check3: - 323 # else rewind-stream(line) - 324 # . rewind-stream(line) - 325 # . . push args - 326 51/push-ECX - 327 # . . call - 328 e8/call rewind-stream/disp32 - 329 # . . discard args - 330 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 331 # if (in-code? != 0) convert-instruction(line, out) - 332 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 333 74/jump-if-equal $convert:data/disp8 - 334 $convert:code: - 335 # . convert-instruction(line, out) - 336 # . . push args - 337 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 338 51/push-ECX - 339 # . . call - 340 e8/call convert-instruction/disp32 - 341 # . . discard args - 342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 343 # . loop - 344 e9/jump $convert:loop/disp32 - 345 $convert:data: - 346 # else convert-data(line, out) - 347 # . . push args - 348 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 349 51/push-ECX - 350 # . . call - 351 e8/call convert-data/disp32 - 352 # . . discard args - 353 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 354 # . loop - 355 e9/jump $convert:loop/disp32 - 356 $convert:pass-through: - 357 # write-stream-data(out, line) - 358 # . . push args - 359 51/push-ECX - 360 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 361 # . . call - 362 e8/call write-stream-data/disp32 - 363 # . . discard args - 364 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 365 # . loop - 366 e9/jump $convert:loop/disp32 - 367 $convert:break: - 368 # flush(out) - 369 # . . push args - 370 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 371 # . . call - 372 e8/call flush/disp32 - 373 # . . discard args - 374 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 375 $convert:end: - 376 # . reclaim locals - 377 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP - 378 # . restore registers - 379 5b/pop-to-EBX - 380 5a/pop-to-EDX - 381 59/pop-to-ECX - 382 58/pop-to-EAX - 383 # . epilog - 384 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 385 5d/pop-to-EBP - 386 c3/return - 387 - 388 test-convert-passes-empty-lines-through: - 389 # if a line is empty, pass it along unchanged - 390 # . prolog - 391 55/push-EBP - 392 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 393 # setup - 394 # . clear-stream(_test-input-stream) - 395 # . . push args - 396 68/push _test-input-stream/imm32 - 397 # . . call - 398 e8/call clear-stream/disp32 - 399 # . . discard args - 400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 401 # . clear-stream(_test-input-buffered-file+4) - 402 # . . push args - 403 b8/copy-to-EAX _test-input-buffered-file/imm32 - 404 05/add-to-EAX 4/imm32 - 405 50/push-EAX - 406 # . . call - 407 e8/call clear-stream/disp32 - 408 # . . discard args - 409 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 410 # . clear-stream(_test-output-stream) - 411 # . . push args - 412 68/push _test-output-stream/imm32 - 413 # . . call - 414 e8/call clear-stream/disp32 - 415 # . . discard args - 416 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 417 # . clear-stream(_test-output-buffered-file+4) - 418 # . . push args - 419 b8/copy-to-EAX _test-output-buffered-file/imm32 - 420 05/add-to-EAX 4/imm32 - 421 50/push-EAX - 422 # . . call - 423 e8/call clear-stream/disp32 - 424 # . . discard args - 425 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 426 # write nothing to input - 427 # convert(_test-input-buffered-file, _test-output-buffered-file) - 428 # . . push args - 429 68/push _test-output-buffered-file/imm32 - 430 68/push _test-input-buffered-file/imm32 - 431 # . . call - 432 e8/call convert/disp32 - 433 # . . discard args - 434 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 435 # check that the line just passed through - 436 # . flush(_test-output-buffered-file) - 437 # . . push args - 438 68/push _test-output-buffered-file/imm32 - 439 # . . call - 440 e8/call flush/disp32 - 441 # . . discard args - 442 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 443 # . check-stream-equal(_test-output-stream, "", msg) - 444 # . . push args - 445 68/push "F - test-convert-passes-empty-lines-through"/imm32 - 446 68/push ""/imm32 - 447 68/push _test-output-stream/imm32 - 448 # . . call - 449 e8/call check-stream-equal/disp32 - 450 # . . discard args - 451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 452 # . epilog - 453 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 454 5d/pop-to-EBP - 455 c3/return - 456 - 457 test-convert-passes-lines-with-just-whitespace-through: - 458 # if a line is empty, pass it along unchanged - 459 # . prolog - 460 55/push-EBP - 461 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 462 # setup - 463 # . clear-stream(_test-input-stream) - 464 # . . push args - 465 68/push _test-input-stream/imm32 - 466 # . . call - 467 e8/call clear-stream/disp32 - 468 # . . discard args - 469 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 470 # . clear-stream(_test-input-buffered-file+4) - 471 # . . push args - 472 b8/copy-to-EAX _test-input-buffered-file/imm32 - 473 05/add-to-EAX 4/imm32 - 474 50/push-EAX - 475 # . . call - 476 e8/call clear-stream/disp32 - 477 # . . discard args - 478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 479 # . clear-stream(_test-output-stream) - 480 # . . push args - 481 68/push _test-output-stream/imm32 - 482 # . . call - 483 e8/call clear-stream/disp32 - 484 # . . discard args - 485 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 486 # . clear-stream(_test-output-buffered-file+4) - 487 # . . push args - 488 b8/copy-to-EAX _test-output-buffered-file/imm32 - 489 05/add-to-EAX 4/imm32 - 490 50/push-EAX - 491 # . . call - 492 e8/call clear-stream/disp32 - 493 # . . discard args - 494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 495 # initialize input - 496 # . write(_test-input-stream, " ") - 497 # . . push args - 498 68/push " "/imm32 - 499 68/push _test-input-stream/imm32 - 500 # . . call - 501 e8/call write/disp32 - 502 # . . discard args - 503 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 504 # convert(_test-input-buffered-file, _test-output-buffered-file) - 505 # . . push args - 506 68/push _test-output-buffered-file/imm32 - 507 68/push _test-input-buffered-file/imm32 - 508 # . . call - 509 e8/call convert/disp32 - 510 # . . discard args - 511 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 512 # check that the line just passed through - 513 # . flush(_test-output-buffered-file) - 514 # . . push args - 515 68/push _test-output-buffered-file/imm32 - 516 # . . call - 517 e8/call flush/disp32 - 518 # . . discard args - 519 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 520 # . check-next-stream-line-equal(_test-output-stream, " ", msg) - 521 # . . push args - 522 68/push "F - test-convert-passes-with-just-whitespace-through"/imm32 - 523 68/push " "/imm32 - 524 68/push _test-output-stream/imm32 - 525 # . . call - 526 e8/call check-next-stream-line-equal/disp32 - 527 # . . discard args - 528 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 529 # . epilog - 530 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 531 5d/pop-to-EBP - 532 c3/return - 533 - 534 test-convert-passes-segment-headers-through: - 535 # if a line starts with '==', pass it along unchanged - 536 # . prolog - 537 55/push-EBP - 538 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 539 # setup - 540 # . clear-stream(_test-input-stream) - 541 # . . push args - 542 68/push _test-input-stream/imm32 - 543 # . . call - 544 e8/call clear-stream/disp32 - 545 # . . discard args - 546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 547 # . clear-stream(_test-input-buffered-file+4) - 548 # . . push args - 549 b8/copy-to-EAX _test-input-buffered-file/imm32 - 550 05/add-to-EAX 4/imm32 - 551 50/push-EAX - 552 # . . call - 553 e8/call clear-stream/disp32 - 554 # . . discard args - 555 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 556 # . clear-stream(_test-output-stream) - 557 # . . push args - 558 68/push _test-output-stream/imm32 - 559 # . . call - 560 e8/call clear-stream/disp32 - 561 # . . discard args - 562 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 563 # . clear-stream(_test-output-buffered-file+4) - 564 # . . push args - 565 b8/copy-to-EAX _test-output-buffered-file/imm32 - 566 05/add-to-EAX 4/imm32 - 567 50/push-EAX - 568 # . . call - 569 e8/call clear-stream/disp32 - 570 # . . discard args - 571 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 572 # initialize input - 573 # . write(_test-input-stream, "== abcd 0x1") - 574 # . . push args - 575 68/push "== abcd 0x1"/imm32 - 576 68/push _test-input-stream/imm32 - 577 # . . call - 578 e8/call write/disp32 - 579 # . . discard args - 580 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 581 # convert(_test-input-buffered-file, _test-output-buffered-file) - 582 # . . push args - 583 68/push _test-output-buffered-file/imm32 - 584 68/push _test-input-buffered-file/imm32 - 585 # . . call - 586 e8/call convert/disp32 - 587 # . . discard args - 588 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 589 # check that the line just passed through - 590 # . flush(_test-output-buffered-file) - 591 # . . push args - 592 68/push _test-output-buffered-file/imm32 - 593 # . . call - 594 e8/call flush/disp32 - 595 # . . discard args - 596 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 597 # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg) - 598 # . . push args - 599 68/push "F - test-convert-passes-segment-headers-through"/imm32 - 600 68/push "== abcd 0x1"/imm32 - 601 68/push _test-output-stream/imm32 - 602 # . . call - 603 e8/call check-stream-equal/disp32 - 604 # . . discard args - 605 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 606 # . epilog - 607 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 608 5d/pop-to-EBP - 609 c3/return - 610 - 611 test-convert-in-data-segment: - 612 # correctly process lines in the data segment - 613 # . prolog - 614 55/push-EBP - 615 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 616 # setup - 617 # . clear-stream(_test-input-stream) - 618 # . . push args - 619 68/push _test-input-stream/imm32 - 620 # . . call - 621 e8/call clear-stream/disp32 - 622 # . . discard args - 623 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 624 # . clear-stream(_test-input-buffered-file+4) - 625 # . . push args - 626 b8/copy-to-EAX _test-input-buffered-file/imm32 - 627 05/add-to-EAX 4/imm32 - 628 50/push-EAX - 629 # . . call - 630 e8/call clear-stream/disp32 - 631 # . . discard args - 632 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 633 # . clear-stream(_test-output-stream) - 634 # . . push args - 635 68/push _test-output-stream/imm32 - 636 # . . call - 637 e8/call clear-stream/disp32 - 638 # . . discard args - 639 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 640 # . clear-stream(_test-output-buffered-file+4) - 641 # . . push args - 642 b8/copy-to-EAX _test-output-buffered-file/imm32 - 643 05/add-to-EAX 4/imm32 - 644 50/push-EAX - 645 # . . call - 646 e8/call clear-stream/disp32 - 647 # . . discard args - 648 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 649 # initialize input - 650 # == code 0x1 - 651 # == data 0x2 - 652 # 3 4/imm32 - 653 # . write(_test-input-stream, "== code 0x1") - 654 # . . push args - 655 68/push "== code 0x1\n"/imm32 - 656 68/push _test-input-stream/imm32 - 657 # . . call - 658 e8/call write/disp32 - 659 # . . discard args - 660 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 661 # . write(_test-input-stream, "== data 0x2\n") - 662 # . . push args - 663 68/push "== data 0x2\n"/imm32 - 664 68/push _test-input-stream/imm32 - 665 # . . call - 666 e8/call write/disp32 - 667 # . . discard args - 668 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 669 # . write(_test-input-stream, "3 4/imm32\n") - 670 # . . push args - 671 68/push "3 4/imm32\n"/imm32 - 672 68/push _test-input-stream/imm32 - 673 # . . call - 674 e8/call write/disp32 - 675 # . . discard args - 676 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 677 # convert(_test-input-buffered-file, _test-output-buffered-file) - 678 # . . push args - 679 68/push _test-output-buffered-file/imm32 - 680 68/push _test-input-buffered-file/imm32 - 681 # . . call - 682 e8/call convert/disp32 - 683 # . . discard args - 684 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 685 # check output - 686 +-- 26 lines: #? # debug print --------------------------------------------------------------------------------------------------------------------------- - 712 # . flush(_test-output-buffered-file) - 713 # . . push args - 714 68/push _test-output-buffered-file/imm32 - 715 # . . call - 716 e8/call flush/disp32 - 717 # . . discard args - 718 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 719 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) - 720 # . . push args - 721 68/push "F - test-convert-in-data-segment/0"/imm32 - 722 68/push "== code 0x1"/imm32 - 723 68/push _test-output-stream/imm32 - 724 # . . call - 725 e8/call check-next-stream-line-equal/disp32 - 726 # . . discard args - 727 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 728 # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) - 729 # . . push args - 730 68/push "F - test-convert-in-data-segment/1"/imm32 - 731 68/push "== data 0x2"/imm32 - 732 68/push _test-output-stream/imm32 - 733 # . . call - 734 e8/call check-next-stream-line-equal/disp32 - 735 # . . discard args - 736 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 737 # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) - 738 # . . push args - 739 68/push "F - test-convert-in-data-segment/2"/imm32 - 740 68/push "03 04 00 00 00 "/imm32 - 741 68/push _test-output-stream/imm32 - 742 # . . call - 743 e8/call check-next-stream-line-equal/disp32 - 744 # . . discard args - 745 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 746 # . epilog - 747 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 748 5d/pop-to-EBP - 749 c3/return - 750 - 751 test-convert-code-and-data-segments: - 752 # correctly process lines in both code and data segments - 753 # . prolog - 754 55/push-EBP - 755 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 756 # setup - 757 # . clear-stream(_test-input-stream) - 758 # . . push args - 759 68/push _test-input-stream/imm32 - 760 # . . call - 761 e8/call clear-stream/disp32 - 762 # . . discard args - 763 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 764 # . clear-stream(_test-input-buffered-file+4) - 765 # . . push args - 766 b8/copy-to-EAX _test-input-buffered-file/imm32 - 767 05/add-to-EAX 4/imm32 - 768 50/push-EAX - 769 # . . call - 770 e8/call clear-stream/disp32 - 771 # . . discard args - 772 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 773 # . clear-stream(_test-output-stream) - 774 # . . push args - 775 68/push _test-output-stream/imm32 - 776 # . . call - 777 e8/call clear-stream/disp32 - 778 # . . discard args - 779 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 780 # . clear-stream(_test-output-buffered-file+4) - 781 # . . push args - 782 b8/copy-to-EAX _test-output-buffered-file/imm32 - 783 05/add-to-EAX 4/imm32 - 784 50/push-EAX - 785 # . . call - 786 e8/call clear-stream/disp32 - 787 # . . discard args - 788 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 789 # initialize input - 790 # == code 0x1 - 791 # e8/call 20/disp32 - 792 # 68/push 0x20/imm8 - 793 # == data 0x2 - 794 # 3 4/imm32 - 795 # . write(_test-input-stream, "== code 0x1\n") - 796 # . . push args - 797 68/push "== code 0x1\n"/imm32 - 798 68/push _test-input-stream/imm32 - 799 # . . call - 800 e8/call write/disp32 - 801 # . . discard args - 802 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 803 # . write(_test-input-stream, "e8/call 20/disp32\n") - 804 # . . push args - 805 68/push "e8/call 20/disp32\n"/imm32 - 806 68/push _test-input-stream/imm32 - 807 # . . call - 808 e8/call write/disp32 - 809 # . . discard args - 810 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 811 # . write(_test-input-stream, "68/push 0x20/imm8\n") - 812 # . . push args - 813 68/push "68/push 0x20/imm8\n"/imm32 - 814 68/push _test-input-stream/imm32 - 815 # . . call - 816 e8/call write/disp32 - 817 # . . discard args - 818 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 819 # . write(_test-input-stream, "== data 0x2\n") - 820 # . . push args - 821 68/push "== data 0x2\n"/imm32 - 822 68/push _test-input-stream/imm32 - 823 # . . call - 824 e8/call write/disp32 - 825 # . . discard args - 826 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 827 # . write(_test-input-stream, "3 4/imm32\n") - 828 # . . push args - 829 68/push "3 4/imm32\n"/imm32 - 830 68/push _test-input-stream/imm32 - 831 # . . call - 832 e8/call write/disp32 - 833 # . . discard args - 834 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 835 # convert(_test-input-buffered-file, _test-output-buffered-file) - 836 # . . push args - 837 68/push _test-output-buffered-file/imm32 - 838 68/push _test-input-buffered-file/imm32 - 839 # . . call - 840 e8/call convert/disp32 - 841 # . . discard args - 842 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 843 # check output - 844 # == code 0x1 - 845 # e8 20 00 00 00 # e8/call 20/disp32 - 846 # 68 20 # 68/push 0x20/imm8 - 847 # == data 0x2 - 848 # 03 04 00 00 00 - 849 +-- 26 lines: #? # debug print --------------------------------------------------------------------------------------------------------------------------- - 875 # . flush(_test-output-buffered-file) - 876 # . . push args - 877 68/push _test-output-buffered-file/imm32 - 878 # . . call - 879 e8/call flush/disp32 - 880 # . . discard args - 881 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 882 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) - 883 # . . push args - 884 68/push "F - test-convert-code-and-data-segments/0"/imm32 - 885 68/push "== code 0x1"/imm32 - 886 68/push _test-output-stream/imm32 - 887 # . . call - 888 e8/call check-next-stream-line-equal/disp32 - 889 # . . discard args - 890 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 891 # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg) - 892 # . . push args - 893 68/push "F - test-convert-code-and-data-segments/1"/imm32 - 894 68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32 - 895 68/push _test-output-stream/imm32 - 896 # . . call - 897 e8/call check-next-stream-line-equal/disp32 - 898 # . . discard args - 899 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 900 # . check-next-stream-line-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg) - 901 # . . push args - 902 68/push "F - test-convert-code-and-data-segments/2"/imm32 - 903 68/push "68 20 # 68/push 0x20/imm8"/imm32 - 904 68/push _test-output-stream/imm32 - 905 # . . call - 906 e8/call check-next-stream-line-equal/disp32 - 907 # . . discard args - 908 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 909 # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) - 910 # . . push args - 911 68/push "F - test-convert-code-and-data-segments/3"/imm32 - 912 68/push "== data 0x2"/imm32 - 913 68/push _test-output-stream/imm32 - 914 # . . call - 915 e8/call check-next-stream-line-equal/disp32 - 916 # . . discard args - 917 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 918 # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) - 919 # . . push args - 920 68/push "F - test-convert-code-and-data-segments/4"/imm32 - 921 68/push "03 04 00 00 00 "/imm32 - 922 68/push _test-output-stream/imm32 - 923 # . . call - 924 e8/call check-next-stream-line-equal/disp32 - 925 # . . discard args - 926 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 927 # . epilog - 928 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 929 5d/pop-to-EBP - 930 c3/return - 931 - 932 convert-data: # line : (address stream byte), out : (address buffered-file) -> <void> - 933 # pseudocode: - 934 # var word-slice = {0, 0} - 935 # while true - 936 # word-slice = next-word(line) - 937 # if slice-empty?(word-slice) # end of file (maybe including trailing whitespace) - 938 # break # skip emitting some whitespace - 939 # if slice-starts-with?(word-slice, "#") # comment - 940 # write-slice-buffered(out, word-slice) - 941 # break - 942 # if slice-ends-with?(word-slice, ":") # label - 943 # write-stream-data(out, line) - 944 # break - 945 # if has-metadata?(word-slice, "imm32") - 946 # emit(out, word-slice, 4) - 947 # # disp32 is not permitted in data segments, and anything else is only a byte long - 948 # else - 949 # emit(out, word-slice, 1) - 950 # - 951 # . prolog - 952 55/push-EBP - 953 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 954 # . save registers - 955 50/push-EAX - 956 51/push-ECX - 957 52/push-EDX - 958 # var word-slice/ECX = {0, 0} - 959 68/push 0/imm32/end - 960 68/push 0/imm32/start - 961 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 962 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- - 988 $convert-data:loop: - 989 # next-word(line, word-slice) - 990 # . . push args - 991 51/push-ECX - 992 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 993 # . . call - 994 e8/call next-word/disp32 - 995 # . . discard args - 996 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 997 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- -1039 $convert-data:check0: -1040 # if (slice-empty?(word-slice)) break -1041 # . EAX = slice-empty?(word-slice) -1042 # . . push args -1043 51/push-ECX -1044 # . . call -1045 e8/call slice-empty?/disp32 -1046 # . . discard args -1047 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1048 # . if (EAX != 0) break -1049 3d/compare-EAX-and 0/imm32 -1050 0f 85/jump-if-not-equal $convert-data:break/disp32 -1051 $convert-data:check-for-comment: -1052 # if (slice-starts-with?(word-slice, "#")) -1053 # . start/EDX = word-slice->start -1054 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX -1055 # . c/EAX = *start -1056 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1057 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL -1058 # . if (EAX != '#') goto next check -1059 3d/compare-EAX-and 0x23/imm32/hash -1060 75/jump-if-not-equal $convert-data:check-for-label/disp8 -1061 $convert-data:comment: -1062 # write-slice-buffered(out, word-slice) -1063 # . . push args -1064 51/push-ECX -1065 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1066 # . . call -1067 e8/call write-slice-buffered/disp32 -1068 # . . discard args -1069 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1070 # break -1071 75/jump-if-not-equal $convert-data:break/disp8 -1072 $convert-data:check-for-label: -1073 # if (slice-ends-with?(word-slice, ":")) -1074 # . end/EDX = word-slice->end -1075 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX -1076 # . c/EAX = *(end-1) -1077 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1078 8a/copy-byte 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/AL -1/disp8 . # copy byte at *ECX to AL -1079 # . if (EAX != ':') goto next check -1080 3d/compare-EAX-and 0x3a/imm32/colon -1081 75/jump-if-not-equal $convert-data:check-for-imm32/disp8 -1082 $convert-data:label: -1083 # write-stream-data(out, line) -1084 # . . push args -1085 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1086 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1087 # . . call -1088 e8/call write-stream-data/disp32 -1089 # . . discard args -1090 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1091 # break -1092 75/jump-if-not-equal $convert-data:break/disp8 -1093 $convert-data:check-for-imm32: -1094 # if (has-metadata?(word-slice, "imm32")) -1095 # . EAX = has-metadata?(ECX, "imm32") -1096 # . . push args -1097 68/push "imm32"/imm32 -1098 51/push-ECX -1099 # . . call -1100 e8/call has-metadata?/disp32 -1101 # . . discard args -1102 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1103 # . if (EAX == 0) process as a single byte -1104 3d/compare-EAX-and 0/imm32 -1105 74/jump-if-equal $convert-data:single-byte/disp8 -1106 $convert-data:imm32: -1107 # emit(out, word-slice, 4) -1108 # . . push args -1109 68/push 4/imm32 -1110 51/push-ECX -1111 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1112 # . . call -1113 e8/call emit/disp32 -1114 # . . discard args -1115 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1116 e9/jump $convert-data:loop/disp32 -1117 $convert-data:single-byte: -1118 # emit(out, word-slice, 1) -1119 # . . push args -1120 68/push 1/imm32 -1121 51/push-ECX -1122 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1123 # . . call -1124 e8/call emit/disp32 -1125 # . . discard args -1126 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1127 e9/jump $convert-data:loop/disp32 -1128 $convert-data:break: -1129 $convert-data:end: -1130 # . reclaim locals -1131 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1132 # . restore registers -1133 5a/pop-to-EDX -1134 59/pop-to-ECX -1135 58/pop-to-EAX -1136 # . epilog -1137 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1138 5d/pop-to-EBP -1139 c3/return -1140 -1141 test-convert-data-passes-comments-through: -1142 # if a line starts with '#', pass it along unchanged -1143 # . prolog -1144 55/push-EBP -1145 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1146 # setup -1147 # . clear-stream(_test-input-stream) -1148 # . . push args -1149 68/push _test-input-stream/imm32 -1150 # . . call -1151 e8/call clear-stream/disp32 -1152 # . . discard args -1153 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1154 # . clear-stream(_test-output-stream) -1155 # . . push args -1156 68/push _test-output-stream/imm32 -1157 # . . call -1158 e8/call clear-stream/disp32 -1159 # . . discard args -1160 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1161 # . clear-stream(_test-output-buffered-file+4) -1162 # . . push args -1163 b8/copy-to-EAX _test-output-buffered-file/imm32 -1164 05/add-to-EAX 4/imm32 -1165 50/push-EAX -1166 # . . call -1167 e8/call clear-stream/disp32 -1168 # . . discard args -1169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1170 # initialize input -1171 # . write(_test-input-stream, "# abcd") -1172 # . . push args -1173 68/push "# abcd"/imm32 -1174 68/push _test-input-stream/imm32 -1175 # . . call -1176 e8/call write/disp32 -1177 # . . discard args -1178 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1179 # convert-data(_test-input-stream, _test-output-buffered-file) -1180 # . . push args -1181 68/push _test-output-buffered-file/imm32 -1182 68/push _test-input-stream/imm32 -1183 # . . call -1184 e8/call convert-data/disp32 -1185 # . . discard args -1186 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1187 # check that the line just passed through -1188 # . flush(_test-output-buffered-file) -1189 # . . push args -1190 68/push _test-output-buffered-file/imm32 -1191 # . . call -1192 e8/call flush/disp32 -1193 # . . discard args -1194 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1195 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1221 # . check-stream-equal(_test-output-stream, "# abcd", msg) -1222 # . . push args -1223 68/push "F - test-convert-data-passes-comments-through"/imm32 -1224 68/push "# abcd"/imm32 -1225 68/push _test-output-stream/imm32 -1226 # . . call -1227 e8/call check-stream-equal/disp32 -1228 # . . discard args -1229 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1230 # . epilog -1231 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1232 5d/pop-to-EBP -1233 c3/return -1234 -1235 test-convert-data-passes-labels-through: -1236 # if the first word ends with ':', pass along the entire line unchanged -1237 # . prolog -1238 55/push-EBP -1239 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1240 # setup -1241 # . clear-stream(_test-input-stream) -1242 # . . push args -1243 68/push _test-input-stream/imm32 -1244 # . . call -1245 e8/call clear-stream/disp32 -1246 # . . discard args -1247 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1248 # . clear-stream(_test-output-stream) -1249 # . . push args -1250 68/push _test-output-stream/imm32 -1251 # . . call -1252 e8/call clear-stream/disp32 -1253 # . . discard args -1254 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1255 # . clear-stream(_test-output-buffered-file+4) -1256 # . . push args -1257 b8/copy-to-EAX _test-output-buffered-file/imm32 -1258 05/add-to-EAX 4/imm32 -1259 50/push-EAX -1260 # . . call -1261 e8/call clear-stream/disp32 -1262 # . . discard args -1263 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1264 # initialize input -1265 # . write(_test-input-stream, "ab: # cd") -1266 # . . push args -1267 68/push "ab: # cd"/imm32 -1268 68/push _test-input-stream/imm32 -1269 # . . call -1270 e8/call write/disp32 -1271 # . . discard args -1272 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1273 # convert-data(_test-input-stream, _test-output-buffered-file) -1274 # . . push args -1275 68/push _test-output-buffered-file/imm32 -1276 68/push _test-input-stream/imm32 -1277 # . . call -1278 e8/call convert-data/disp32 -1279 # . . discard args -1280 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1281 # check that the line just passed through -1282 # . flush(_test-output-buffered-file) -1283 # . . push args -1284 68/push _test-output-buffered-file/imm32 -1285 # . . call -1286 e8/call flush/disp32 -1287 # . . discard args -1288 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1289 # . check-stream-equal(_test-output-stream, "ab: # cd", msg) -1290 # . . push args -1291 68/push "F - test-convert-data-passes-labels-through"/imm32 -1292 68/push "ab: # cd"/imm32 -1293 68/push _test-output-stream/imm32 -1294 # . . call -1295 e8/call check-stream-equal/disp32 -1296 # . . discard args -1297 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1298 # . epilog -1299 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1300 5d/pop-to-EBP -1301 c3/return -1302 -1303 test-convert-data-passes-names-through: -1304 # If a word is a valid name, just emit it unchanged. -1305 # Later phases will deal with it. -1306 # . prolog -1307 55/push-EBP -1308 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1309 # setup -1310 # . clear-stream(_test-input-stream) -1311 # . . push args -1312 68/push _test-input-stream/imm32 -1313 # . . call -1314 e8/call clear-stream/disp32 -1315 # . . discard args -1316 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1317 # . clear-stream(_test-output-stream) -1318 # . . push args -1319 68/push _test-output-stream/imm32 -1320 # . . call -1321 e8/call clear-stream/disp32 -1322 # . . discard args -1323 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1324 # . clear-stream(_test-output-buffered-file+4) -1325 # . . push args -1326 b8/copy-to-EAX _test-output-buffered-file/imm32 -1327 05/add-to-EAX 4/imm32 -1328 50/push-EAX -1329 # . . call -1330 e8/call clear-stream/disp32 -1331 # . . discard args -1332 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1333 # initialize input -1334 # . write(_test-input-stream, "abcd/imm32") -1335 # . . push args -1336 68/push "abcd/imm32"/imm32 -1337 68/push _test-input-stream/imm32 -1338 # . . call -1339 e8/call write/disp32 -1340 # . . discard args -1341 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1342 # convert-data(_test-input-stream, _test-output-buffered-file) -1343 # . . push args -1344 68/push _test-output-buffered-file/imm32 -1345 68/push _test-input-stream/imm32 -1346 # . . call -1347 e8/call convert-data/disp32 -1348 # . . discard args -1349 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1350 # check that the line just passed through -1351 # . flush(_test-output-buffered-file) -1352 # . . push args -1353 68/push _test-output-buffered-file/imm32 -1354 # . . call -1355 e8/call flush/disp32 -1356 # . . discard args -1357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1358 # . check-stream-equal(_test-output-stream, "abcd/imm32", msg) -1359 # . . push args -1360 68/push "F - test-convert-data-passes-names-through"/imm32 -1361 68/push "abcd/imm32 "/imm32 -1362 68/push _test-output-stream/imm32 -1363 # . . call -1364 e8/call check-stream-equal/disp32 -1365 # . . discard args -1366 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1367 # . epilog -1368 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1369 5d/pop-to-EBP -1370 c3/return -1371 -1372 test-convert-data-handles-imm32: -1373 # If a word has the /imm32 metadata, emit it in 4 bytes. -1374 # . prolog -1375 55/push-EBP -1376 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1377 # setup -1378 # . clear-stream(_test-input-stream) -1379 # . . push args -1380 68/push _test-input-stream/imm32 -1381 # . . call -1382 e8/call clear-stream/disp32 -1383 # . . discard args -1384 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1385 # . clear-stream(_test-output-stream) -1386 # . . push args -1387 68/push _test-output-stream/imm32 -1388 # . . call -1389 e8/call clear-stream/disp32 -1390 # . . discard args -1391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1392 # . clear-stream(_test-output-buffered-file+4) -1393 # . . push args -1394 b8/copy-to-EAX _test-output-buffered-file/imm32 -1395 05/add-to-EAX 4/imm32 -1396 50/push-EAX -1397 # . . call -1398 e8/call clear-stream/disp32 -1399 # . . discard args -1400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1401 # initialize input -1402 # . write(_test-input-stream, "30/imm32") -1403 # . . push args -1404 68/push "30/imm32"/imm32 -1405 68/push _test-input-stream/imm32 -1406 # . . call -1407 e8/call write/disp32 -1408 # . . discard args -1409 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1410 # convert-data(_test-input-stream, _test-output-buffered-file) -1411 # . . push args -1412 68/push _test-output-buffered-file/imm32 -1413 68/push _test-input-stream/imm32 -1414 # . . call -1415 e8/call convert-data/disp32 -1416 # . . discard args -1417 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1418 # check that 4 bytes were written -1419 # . flush(_test-output-buffered-file) -1420 # . . push args -1421 68/push _test-output-buffered-file/imm32 -1422 # . . call -1423 e8/call flush/disp32 -1424 # . . discard args -1425 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1426 # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg) -1427 # . . push args -1428 68/push "F - test-convert-data-handles-imm32"/imm32 -1429 68/push "30 00 00 00 "/imm32 -1430 68/push _test-output-stream/imm32 -1431 # . . call -1432 e8/call check-stream-equal/disp32 -1433 # . . discard args -1434 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1435 # . epilog -1436 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1437 5d/pop-to-EBP -1438 c3/return -1439 -1440 test-convert-data-handles-single-byte: -1441 # Any metadata but /imm32 will emit a single byte. -1442 # Data segments can't have /disp32, and SubX doesn't support 16-bit operands. -1443 # . prolog -1444 55/push-EBP -1445 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1446 # setup -1447 # . clear-stream(_test-input-stream) -1448 # . . push args -1449 68/push _test-input-stream/imm32 -1450 # . . call -1451 e8/call clear-stream/disp32 -1452 # . . discard args -1453 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1454 # . clear-stream(_test-output-stream) -1455 # . . push args -1456 68/push _test-output-stream/imm32 -1457 # . . call -1458 e8/call clear-stream/disp32 -1459 # . . discard args -1460 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1461 # . clear-stream(_test-output-buffered-file+4) -1462 # . . push args -1463 b8/copy-to-EAX _test-output-buffered-file/imm32 -1464 05/add-to-EAX 4/imm32 -1465 50/push-EAX -1466 # . . call -1467 e8/call clear-stream/disp32 -1468 # . . discard args -1469 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1470 # initialize input -1471 # . write(_test-input-stream, "30/imm16") -1472 # . . push args -1473 68/push "30/imm16"/imm32 -1474 68/push _test-input-stream/imm32 -1475 # . . call -1476 e8/call write/disp32 -1477 # . . discard args -1478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1479 # convert-data(_test-input-stream, _test-output-buffered-file) -1480 # . . push args -1481 68/push _test-output-buffered-file/imm32 -1482 68/push _test-input-stream/imm32 -1483 # . . call -1484 e8/call convert-data/disp32 -1485 # . . discard args -1486 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1487 # check that a single byte was written (imm16 is not a valid operand type) -1488 # . flush(_test-output-buffered-file) -1489 # . . push args -1490 68/push _test-output-buffered-file/imm32 -1491 # . . call -1492 e8/call flush/disp32 -1493 # . . discard args -1494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1495 # . check-stream-equal(_test-output-stream, "30 ", msg) -1496 # . . push args -1497 68/push "F - test-convert-data-handles-single-byte"/imm32 -1498 68/push "30 "/imm32 -1499 68/push _test-output-stream/imm32 -1500 # . . call -1501 e8/call check-stream-equal/disp32 -1502 # . . discard args -1503 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1504 # . epilog -1505 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1506 5d/pop-to-EBP -1507 c3/return -1508 -1509 test-convert-data-multiple-bytes: -1510 # Multiple single-byte words in input stream get processed one by one. -1511 # . prolog -1512 55/push-EBP -1513 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1514 # setup -1515 # . clear-stream(_test-input-stream) -1516 # . . push args -1517 68/push _test-input-stream/imm32 -1518 # . . call -1519 e8/call clear-stream/disp32 -1520 # . . discard args -1521 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1522 # . clear-stream(_test-output-stream) -1523 # . . push args -1524 68/push _test-output-stream/imm32 -1525 # . . call -1526 e8/call clear-stream/disp32 -1527 # . . discard args -1528 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1529 # . clear-stream(_test-output-buffered-file+4) -1530 # . . push args -1531 b8/copy-to-EAX _test-output-buffered-file/imm32 -1532 05/add-to-EAX 4/imm32 -1533 50/push-EAX -1534 # . . call -1535 e8/call clear-stream/disp32 -1536 # . . discard args -1537 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1538 # initialize input -1539 # . write(_test-input-stream, "1 2") -1540 # . . push args -1541 68/push "1 2"/imm32 -1542 68/push _test-input-stream/imm32 -1543 # . . call -1544 e8/call write/disp32 -1545 # . . discard args -1546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1547 # convert-data(_test-input-stream, _test-output-buffered-file) -1548 # . . push args -1549 68/push _test-output-buffered-file/imm32 -1550 68/push _test-input-stream/imm32 -1551 # . . call -1552 e8/call convert-data/disp32 -1553 # . . discard args -1554 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1555 # check output -1556 # . flush(_test-output-buffered-file) -1557 # . . push args -1558 68/push _test-output-buffered-file/imm32 -1559 # . . call -1560 e8/call flush/disp32 -1561 # . . discard args -1562 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1563 # . check-stream-equal(_test-output-stream, "01 02 ", msg) -1564 # . . push args -1565 68/push "F - test-convert-data-multiple-bytes"/imm32 -1566 68/push "01 02 "/imm32 -1567 68/push _test-output-stream/imm32 -1568 # . . call -1569 e8/call check-stream-equal/disp32 -1570 # . . discard args -1571 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1572 # . epilog -1573 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1574 5d/pop-to-EBP -1575 c3/return -1576 -1577 test-convert-data-byte-then-name: -1578 # Single-byte word followed by valid name get processed one by one. -1579 # . prolog -1580 55/push-EBP -1581 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1582 # setup -1583 # . clear-stream(_test-input-stream) -1584 # . . push args -1585 68/push _test-input-stream/imm32 -1586 # . . call -1587 e8/call clear-stream/disp32 -1588 # . . discard args -1589 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1590 # . clear-stream(_test-output-stream) -1591 # . . push args -1592 68/push _test-output-stream/imm32 -1593 # . . call -1594 e8/call clear-stream/disp32 -1595 # . . discard args -1596 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1597 # . clear-stream(_test-output-buffered-file+4) -1598 # . . push args -1599 b8/copy-to-EAX _test-output-buffered-file/imm32 -1600 05/add-to-EAX 4/imm32 -1601 50/push-EAX -1602 # . . call -1603 e8/call clear-stream/disp32 -1604 # . . discard args -1605 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1606 # initialize input -1607 # . write(_test-input-stream, "30 abcd/o") -1608 # . . push args -1609 68/push "30 abcd/o"/imm32 -1610 68/push _test-input-stream/imm32 -1611 # . . call -1612 e8/call write/disp32 -1613 # . . discard args -1614 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1615 # convert-data(_test-input-stream, _test-output-buffered-file) -1616 # . . push args -1617 68/push _test-output-buffered-file/imm32 -1618 68/push _test-input-stream/imm32 -1619 # . . call -1620 e8/call convert-data/disp32 -1621 # . . discard args -1622 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1623 # check output -1624 # . flush(_test-output-buffered-file) -1625 # . . push args -1626 68/push _test-output-buffered-file/imm32 -1627 # . . call -1628 e8/call flush/disp32 -1629 # . . discard args -1630 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1631 # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg) -1632 # . . push args -1633 68/push "F - test-convert-data-byte-then-name"/imm32 -1634 68/push "30 abcd/o "/imm32 -1635 68/push _test-output-stream/imm32 -1636 # . . call -1637 e8/call check-stream-equal/disp32 -1638 # . . discard args -1639 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1640 # . epilog -1641 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1642 5d/pop-to-EBP -1643 c3/return -1644 -1645 test-convert-data-multiple-words: -1646 # Multiple words in input stream get processed one by one. -1647 # . prolog -1648 55/push-EBP -1649 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1650 # setup -1651 # . clear-stream(_test-input-stream) -1652 # . . push args -1653 68/push _test-input-stream/imm32 -1654 # . . call -1655 e8/call clear-stream/disp32 -1656 # . . discard args -1657 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1658 # . clear-stream(_test-output-stream) -1659 # . . push args -1660 68/push _test-output-stream/imm32 -1661 # . . call -1662 e8/call clear-stream/disp32 -1663 # . . discard args -1664 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1665 # . clear-stream(_test-output-buffered-file+4) -1666 # . . push args -1667 b8/copy-to-EAX _test-output-buffered-file/imm32 -1668 05/add-to-EAX 4/imm32 -1669 50/push-EAX -1670 # . . call -1671 e8/call clear-stream/disp32 -1672 # . . discard args -1673 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1674 # initialize input -1675 # . write(_test-input-stream, "30 abcd/o 42e1/imm32") -1676 # . . push args -1677 68/push "30 abcd/o 42e1/imm32"/imm32 -1678 68/push _test-input-stream/imm32 -1679 # . . call -1680 e8/call write/disp32 -1681 # . . discard args -1682 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1683 # convert-data(_test-input-stream, _test-output-buffered-file) -1684 # . . push args -1685 68/push _test-output-buffered-file/imm32 -1686 68/push _test-input-stream/imm32 -1687 # . . call -1688 e8/call convert-data/disp32 -1689 # . . discard args -1690 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1691 # check output -1692 # . flush(_test-output-buffered-file) -1693 # . . push args -1694 68/push _test-output-buffered-file/imm32 -1695 # . . call -1696 e8/call flush/disp32 -1697 # . . discard args -1698 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1699 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1725 # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg) -1726 # . . push args -1727 68/push "F - test-convert-data-multiple-words"/imm32 -1728 68/push "30 abcd/o e1 42 00 00 "/imm32 -1729 68/push _test-output-stream/imm32 -1730 # . . call -1731 e8/call check-stream-equal/disp32 -1732 # . . discard args -1733 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1734 # . epilog -1735 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1736 5d/pop-to-EBP -1737 c3/return -1738 -1739 test-convert-data-trailing-comment: -1740 # Trailing comments in data segment get appropriately ignored. -1741 # . prolog -1742 55/push-EBP -1743 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1744 # setup -1745 # . clear-stream(_test-input-stream) -1746 # . . push args -1747 68/push _test-input-stream/imm32 -1748 # . . call -1749 e8/call clear-stream/disp32 -1750 # . . discard args -1751 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1752 # . clear-stream(_test-output-stream) -1753 # . . push args -1754 68/push _test-output-stream/imm32 -1755 # . . call -1756 e8/call clear-stream/disp32 -1757 # . . discard args -1758 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1759 # . clear-stream(_test-output-buffered-file+4) -1760 # . . push args -1761 b8/copy-to-EAX _test-output-buffered-file/imm32 -1762 05/add-to-EAX 4/imm32 -1763 50/push-EAX -1764 # . . call -1765 e8/call clear-stream/disp32 -1766 # . . discard args -1767 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1768 # initialize input -1769 # . write(_test-input-stream, "30/imm32 # comment") -1770 # . . push args -1771 68/push "30/imm32 # comment"/imm32 -1772 68/push _test-input-stream/imm32 -1773 # . . call -1774 e8/call write/disp32 -1775 # . . discard args -1776 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1777 # convert-data(_test-input-stream, _test-output-buffered-file) -1778 # . . push args -1779 68/push _test-output-buffered-file/imm32 -1780 68/push _test-input-stream/imm32 -1781 # . . call -1782 e8/call convert-data/disp32 -1783 # . . discard args -1784 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1785 # check output -1786 # . flush(_test-output-buffered-file) -1787 # . . push args -1788 68/push _test-output-buffered-file/imm32 -1789 # . . call -1790 e8/call flush/disp32 -1791 # . . discard args -1792 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1793 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1819 # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg) -1820 # . . push args -1821 68/push "F - test-convert-data-trailing-comment"/imm32 -1822 68/push "30 00 00 00 # comment"/imm32 -1823 68/push _test-output-stream/imm32 -1824 # . . call -1825 e8/call check-stream-equal/disp32 -1826 # . . discard args -1827 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1828 # . epilog -1829 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1830 5d/pop-to-EBP -1831 c3/return -1832 -1833 # pack an instruction, following the C++ version -1834 # -1835 # zero error handling at the moment (continuing to rely on the C++ version for that): -1836 # missing fields are always 0-filled -1837 # bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte. -1838 # may pick up any of duplicate operands in an instruction -1839 # silently drop extraneous operands -1840 # unceremoniously abort on non-numeric operands except disp or imm -1841 # opcodes must be lowercase and zero padded -1842 # opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this. -1843 convert-instruction: # line : (address stream byte), out : (address buffered-file) -> <void> -1844 # pseudocode: -1845 # # some early exits -1846 # var word-slice = next-word(line) -1847 # if slice-empty?(word-slice) -1848 # write-stream-data(out, line) -1849 # return -1850 # if slice-starts-with?(word-slice, "#") -1851 # write-stream-data(out, line) -1852 # return -1853 # if slice-ends-with?(word-slice, ":") -1854 # write-stream-data(out, line) -1855 # return -1856 # # really convert -1857 # emit-opcodes(line, out) -1858 # emit-modrm(line, out) -1859 # emit-sib(line, out) -1860 # emit-disp(line, out) -1861 # emit-imm(line, out) -1862 # emit-line-in-comment(line, out) -1863 # -1864 # . prolog -1865 55/push-EBP -1866 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1867 # . save registers -1868 50/push-EAX -1869 51/push-ECX -1870 52/push-EDX -1871 # var word-slice/ECX = {0, 0} -1872 68/push 0/imm32/end -1873 68/push 0/imm32/start -1874 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1875 # next-word(line, word-slice) -1876 # . . push args -1877 51/push-ECX -1878 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1879 # . . call -1880 e8/call next-word/disp32 -1881 # . . discard args -1882 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1883 $convert-instruction:check0: -1884 # if (slice-empty?(word-slice)) break -1885 # . EAX = slice-empty?(word-slice) -1886 # . . push args -1887 51/push-ECX -1888 # . . call -1889 e8/call slice-empty?/disp32 -1890 # . . discard args -1891 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1892 # . if (EAX != 0) pass through -1893 3d/compare-EAX-and 0/imm32 -1894 75/jump-if-not-equal $convert-instruction:pass-through/disp8 -1895 $convert-instruction:check1: -1896 # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line) -1897 # . start/EDX = word-slice->start -1898 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX -1899 # . c/EAX = *start -1900 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1901 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL -1902 # . if (EAX == '#') pass through -1903 3d/compare-EAX-and 0x23/imm32/hash -1904 74/jump-if-equal $convert-instruction:pass-through/disp8 -1905 $convert-instruction:check2: -1906 # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line) -1907 # . end/EDX = word-slice->end -1908 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX -1909 # . c/EAX = *(end-1) -1910 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1911 8a/copy-byte 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/AL -1/disp8 . # copy byte at *ECX to AL -1912 # . if (EAX == ':') pass through -1913 3d/compare-EAX-and 0x3a/imm32/colon -1914 75/jump-if-not-equal $convert-instruction:really-convert/disp8 -1915 $convert-instruction:pass-through: -1916 # write-stream-data(out, line) -1917 # . . push args -1918 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1919 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1920 # . . call -1921 e8/call write-stream-data/disp32 -1922 # . . discard args -1923 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1924 # return -1925 eb/jump $convert-instruction:end/disp8 -1926 $convert-instruction:really-convert: -1927 # emit-opcodes(line, out) -1928 # . . push args -1929 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1930 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1931 # . . call -1932 e8/call emit-opcodes/disp32 -1933 # . . discard args -1934 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1935 # emit-modrm(line, out) -1936 # . . push args -1937 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1938 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1939 # . . call -1940 e8/call emit-modrm/disp32 -1941 # . . discard args -1942 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1943 # emit-sib(line, out) -1944 # . . push args -1945 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1946 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1947 # . . call -1948 e8/call emit-sib/disp32 -1949 # . . discard args -1950 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1951 # emit-disp(line, out) -1952 # . . push args -1953 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1954 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1955 # . . call -1956 e8/call emit-disp/disp32 -1957 # . . discard args -1958 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1959 # emit-imm(line, out) -1960 # . . push args -1961 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1962 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1963 # . . call -1964 e8/call emit-imm/disp32 -1965 # . . discard args -1966 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1967 # emit-line-in-comment(line, out) -1968 # . . push args -1969 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -1970 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -1971 # . . call -1972 e8/call emit-line-in-comment/disp32 -1973 # . . discard args -1974 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1975 $convert-instruction:end: -1976 # . restore locals -1977 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1978 # . restore registers -1979 5a/pop-to-EDX -1980 59/pop-to-ECX -1981 58/pop-to-EAX -1982 # . epilog -1983 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1984 5d/pop-to-EBP -1985 c3/return -1986 -1987 emit-opcodes: # line : (address stream byte), out : (address buffered-file) -> <void> -1988 # opcodes occupy 1-3 bytes: -1989 # xx -1990 # 0f xx -1991 # f2 xx -1992 # f3 xx -1993 # f2 0f xx -1994 # f3 0f xx -1995 # -1996 # pseudocode: -1997 # rewind-stream(line) -1998 # -1999 # var op1 = next-word(line) -2000 # if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return -2001 # op1 = next-token-from-slice(op1->start, op1->end, "/") -2002 # write-slice-buffered(out, op1) -2003 # if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3") -2004 # return -2005 # -2006 # var op2 = next-word(line) -2007 # if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return -2008 # op2 = next-token-from-slice(op2->start, op2->end, "/") -2009 # write-slice-buffered(out, op2) -2010 # if slice-equal?(op1, "0f") -2011 # return -2012 # if !slice-equal?(op2, "0f") -2013 # return -2014 # -2015 # var op3 = next-word(line) -2016 # if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return -2017 # op3 = next-token-from-slice(op3->start, op3->end, "/") -2018 # write-slice-buffered(out, op3) -2019 # -2020 # . prolog -2021 55/push-EBP -2022 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2023 # . save registers -2024 50/push-EAX -2025 51/push-ECX -2026 52/push-EDX -2027 53/push-EBX -2028 # var op1/ECX = {0, 0} -2029 68/push 0/imm32/end -2030 68/push 0/imm32/start -2031 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -2032 # var op2/EDX = {0, 0} -2033 68/push 0/imm32/end -2034 68/push 0/imm32/start -2035 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX -2036 # rewind-stream(line) -2037 # . . push args -2038 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2039 # . . call -2040 e8/call rewind-stream/disp32 -2041 # . . discard args -2042 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2043 $emit-opcodes:op1: -2044 # next-word(line, op1) -2045 # . . push args -2046 51/push-ECX -2047 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2048 # . . call -2049 e8/call next-word/disp32 -2050 # . . discard args -2051 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2052 # if (slice-empty?(op1)) return -2053 # . EAX = slice-empty?(op1) -2054 # . . push args -2055 51/push-ECX -2056 # . . call -2057 e8/call slice-empty?/disp32 -2058 # . . discard args -2059 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2060 # . if (EAX != 0) return -2061 3d/compare-EAX-and 0/imm32 -2062 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 -2063 # if (slice-starts-with?(op1, "#")) return -2064 # . start/EBX = op1->start -2065 8b/copy 0/mod/indirect 1/rm32/ECX . . . 3/r32/EBX . . # copy *ECX to EBX -2066 # . c/EAX = *start -2067 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2068 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL -2069 # . if (EAX == '#') return -2070 3d/compare-EAX-and 0x23/imm32/hash -2071 0f 84/jump-if-equal $emit-opcodes:end/disp32 -2072 # op1 = next-token-from-slice(op1->start, op1->end, '/') -2073 # . . push args -2074 51/push-ECX -2075 68/push 0x2f/imm32/slash -2076 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) -2077 ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX -2078 # . . call -2079 e8/call next-token-from-slice/disp32 -2080 # . . discard args -2081 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -2082 # write-slice-buffered(out, op1) -2083 # . . push args -2084 51/push-ECX -2085 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2086 # . . call -2087 e8/call write-slice-buffered/disp32 -2088 # . . discard args -2089 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2090 # write-buffered(out, " ") -2091 # . . push args -2092 68/push " "/imm32 -2093 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2094 # . . call -2095 e8/call write-buffered/disp32 -2096 # . . discard args -2097 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2098 # if (slice-equal?(op1, "0f")) goto op2 -2099 # . EAX = slice-equal?(op1, "0f") -2100 # . . push args -2101 68/push "0f"/imm32 -2102 51/push-ECX -2103 # . . call -2104 e8/call slice-equal?/disp32 -2105 # . . discard args -2106 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2107 # . if (EAX != 0) goto op2 -2108 3d/compare-EAX-and 0/imm32 -2109 75/jump-if-not-equal $emit-opcodes:op2/disp8 -2110 # if (slice-equal?(op1, "f2")) goto op2 -2111 # . EAX = slice-equal?(op1, "f2") -2112 # . . push args -2113 68/push "f2"/imm32 -2114 51/push-ECX -2115 # . . call -2116 e8/call slice-equal?/disp32 -2117 # . . discard args -2118 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2119 # . if (EAX != 0) goto op2 -2120 3d/compare-EAX-and 0/imm32 -2121 75/jump-if-not-equal $emit-opcodes:op2/disp8 -2122 # if (slice-equal?(op1, "f3")) goto op2 -2123 # . EAX = slice-equal?(op1, "f3") -2124 # . . push args -2125 68/push "f3"/imm32 -2126 51/push-ECX -2127 # . . call -2128 e8/call slice-equal?/disp32 -2129 # . . discard args -2130 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2131 # . if (EAX != 0) goto op2 -2132 3d/compare-EAX-and 0/imm32 -2133 75/jump-if-not-equal $emit-opcodes:op2/disp8 -2134 # otherwise return -2135 e9/jump $emit-opcodes:end/disp32 -2136 $emit-opcodes:op2: -2137 # next-word(line, op2) -2138 # . . push args -2139 52/push-EDX -2140 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2141 # . . call -2142 e8/call next-word/disp32 -2143 # . . discard args -2144 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2145 # if (slice-empty?(op2)) return -2146 # . EAX = slice-empty?(op2) -2147 # . . push args -2148 52/push-EDX -2149 # . . call -2150 e8/call slice-empty?/disp32 -2151 # . . discard args -2152 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2153 # . if (EAX != 0) return -2154 3d/compare-EAX-and 0/imm32 -2155 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 -2156 # if (slice-starts-with?(op2, "#")) return -2157 # . start/EBX = op2->start -2158 8b/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy *EDX to EBX -2159 # . c/EAX = *start -2160 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2161 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL -2162 # . if (EAX == '#') return -2163 3d/compare-EAX-and 0x23/imm32/hash -2164 0f 84/jump-if-equal $emit-opcodes:end/disp32 -2165 # op2 = next-token-from-slice(op2->start, op2->end, '/') -2166 # . . push args -2167 52/push-EDX -2168 68/push 0x2f/imm32/slash -2169 ff 6/subop/push 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # push *(EDX+4) -2170 ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX -2171 # . . call -2172 e8/call next-token-from-slice/disp32 -2173 # . . discard args -2174 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -2175 # write-slice-buffered(out, op2) -2176 # . . push args -2177 52/push-EDX -2178 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2179 # . . call -2180 e8/call write-slice-buffered/disp32 -2181 # . . discard args -2182 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2183 # write-buffered(out, " ") -2184 # . . push args -2185 68/push " "/imm32 -2186 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2187 # . . call -2188 e8/call write-buffered/disp32 -2189 # . . discard args -2190 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2191 # if (slice-equal?(op1, "0f")) return -2192 # . EAX = slice-equal?(op1, "0f") -2193 # . . push args -2194 68/push "0f"/imm32 -2195 51/push-ECX -2196 # . . call -2197 e8/call slice-equal?/disp32 -2198 # . . discard args -2199 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2200 # . if (EAX != 0) return -2201 3d/compare-EAX-and 0/imm32 -2202 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 -2203 # if (!slice-equal?(op2, "0f")) return -2204 # . EAX = slice-equal?(op2, "0f") -2205 # . . push args -2206 68/push "0f"/imm32 -2207 52/push-EDX -2208 # . . call -2209 e8/call slice-equal?/disp32 -2210 # . . discard args -2211 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2212 # . if (EAX == 0) return -2213 3d/compare-EAX-and 0/imm32 -2214 0f 84/jump-if-equal $emit-opcodes:end/disp32 -2215 $emit-opcodes:op3: -2216 # next-word(line, op3) # reuse op2/EDX -2217 # . . push args -2218 52/push-EDX -2219 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2220 # . . call -2221 e8/call next-word/disp32 -2222 # . . discard args -2223 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2224 # if (slice-empty?(op3)) return -2225 # . EAX = slice-empty?(op3) -2226 # . . push args -2227 52/push-EDX -2228 # . . call -2229 e8/call slice-empty?/disp32 -2230 # . . discard args -2231 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2232 # . if (EAX != 0) return -2233 3d/compare-EAX-and 0/imm32 -2234 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 -2235 # if (slice-starts-with?(op3, "#")) return -2236 # . start/EBX = op2->start -2237 8b/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy *EDX to EBX -2238 # . c/EAX = *start -2239 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2240 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL -2241 # . if (EAX == '#') return -2242 3d/compare-EAX-and 0x23/imm32/hash -2243 0f 84/jump-if-equal $emit-opcodes:end/disp32 -2244 # op3 = next-token-from-slice(op3->start, op3->end, '/') -2245 # . . push args -2246 52/push-EDX -2247 68/push 0x2f/imm32/slash -2248 ff 6/subop/push 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # push *(EDX+4) -2249 ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX -2250 # . . call -2251 e8/call next-token-from-slice/disp32 -2252 # . . discard args -2253 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -2254 # write-slice-buffered(out, op3) -2255 # . . push args -2256 52/push-EDX -2257 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2258 # . . call -2259 e8/call write-slice-buffered/disp32 -2260 # . . discard args -2261 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2262 # write-buffered(out, " ") -2263 # . . push args -2264 68/push " "/imm32 -2265 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2266 # . . call -2267 e8/call write-buffered/disp32 -2268 # . . discard args -2269 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2270 $emit-opcodes:end: -2271 # . restore locals -2272 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -2273 # . restore registers -2274 5b/pop-to-EBX -2275 5a/pop-to-EDX -2276 59/pop-to-ECX -2277 58/pop-to-EAX -2278 # . epilog -2279 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2280 5d/pop-to-EBP -2281 c3/return -2282 -2283 emit-modrm: # line : (address stream byte), out : (address buffered-file) -> <void> -2284 # pseudocode: -2285 # rewind-stream(line) -2286 # var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0 -2287 # var word-slice = {0, 0} -2288 # while true -2289 # word-slice = next-word(line) -2290 # if (slice-empty?(word-slice)) break -2291 # if (slice-starts-with?(word-slice, "#")) break -2292 # if (has-metadata?(word-slice, "mod")) -2293 # mod = parse-hex-int(next-token-from-slice(word-slice, "/")) -2294 # has-modrm? = true -2295 # else if (has-metadata?(word-slice, "rm32")) -2296 # rm32 = parse-hex-int(next-token-from-slice(word-slice, "/")) -2297 # has-modrm? = true -2298 # else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop")) -2299 # r32 = parse-hex-int(next-token-from-slice(word-slice, "/")) -2300 # has-modrm? = true -2301 # if has-modrm? -2302 # var modrm = mod & 0b11 -2303 # modrm <<= 2 -2304 # modrm |= r32 & 0b111 -2305 # modrm <<= 3 -2306 # modrm |= rm32 & 0b111 -2307 # emit-hex(out, modrm, 1) -2308 # -2309 # . prolog -2310 55/push-EBP -2311 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2312 # . save registers -2313 50/push-EAX -2314 51/push-ECX -2315 52/push-EDX -2316 53/push-EBX -2317 56/push-ESI -2318 57/push-EDI -2319 # var word-slice/ECX = {0, 0} -2320 68/push 0/imm32/end -2321 68/push 0/imm32/start -2322 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -2323 # var has-modrm?/EDX = false -2324 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX -2325 # var mod/EBX = 0 -2326 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -2327 # var rm32/ESI = 0 -2328 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI -2329 # var r32/EDI = 0 -2330 31/xor 3/mod/direct 7/rm32/EDI . . . 7/r32/EDI . . # clear EDI -2331 # rewind-stream(line) -2332 # . . push args -2333 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2334 # . . call -2335 e8/call rewind-stream/disp32 -2336 # . . discard args -2337 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2338 $emit-modrm:loop: -2339 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- -2365 # next-word(line, word-slice) -2366 # . . push args -2367 51/push-ECX -2368 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2369 # . . call -2370 e8/call next-word/disp32 -2371 # . . discard args -2372 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2373 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- -2415 $emit-modrm:check0: -2416 # if (slice-empty?(word-slice)) break -2417 # . EAX = slice-empty?(word-slice) -2418 # . . push args -2419 51/push-ECX -2420 # . . call -2421 e8/call slice-empty?/disp32 -2422 # . . discard args -2423 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2424 # . if (EAX != 0) pass through -2425 3d/compare-EAX-and 0/imm32 -2426 0f 85/jump-if-not-equal $emit-modrm:break/disp32 -2427 $emit-modrm:check1: -2428 # if (slice-starts-with?(word-slice, "#")) break -2429 # . spill EDX -2430 52/push-EDX -2431 # . start/EDX = word-slice->start -2432 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX -2433 # . c/EAX = *start -2434 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2435 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL -2436 # . restore EDX -2437 5a/pop-to-EDX -2438 # . if (EAX == '#') pass through -2439 3d/compare-EAX-and 0x23/imm32/hash -2440 0f 84/jump-if-equal $emit-modrm:break/disp32 -2441 $emit-modrm:check-for-mod: -2442 # if (has-metadata?(word-slice, "mod")) -2443 # . EAX = has-metadata?(ECX, "mod") -2444 # . . push args -2445 68/push "mod"/imm32 -2446 51/push-ECX -2447 # . . call -2448 e8/call has-metadata?/disp32 -2449 # . . discard args -2450 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2451 # . if (EAX == 0) goto next check -2452 3d/compare-EAX-and 0/imm32 -2453 74/jump-if-equal $emit-modrm:check-for-rm32/disp8 -2454 $emit-modrm:mod: -2455 # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) -2456 # . EAX = parse-datum-of-word(word-slice) -2457 # . . push args -2458 51/push-ECX -2459 # . . call -2460 e8/call parse-datum-of-word/disp32 -2461 # . . discard args -2462 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2463 # . mod = EAX -2464 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX -2465 # has-modrm? = true -2466 ba/copy-to-EDX 1/imm32/true -2467 # continue -2468 e9/jump $emit-modrm:loop/disp32 -2469 $emit-modrm:check-for-rm32: -2470 # if (has-metadata?(word-slice, "rm32")) -2471 # . EAX = has-metadata?(ECX, "rm32") -2472 # . . push args -2473 68/push "rm32"/imm32 -2474 51/push-ECX -2475 # . . call -2476 e8/call has-metadata?/disp32 -2477 # . . discard args -2478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2479 # . if (EAX == 0) goto next check -2480 3d/compare-EAX-and 0/imm32 -2481 74/jump-if-equal $emit-modrm:check-for-r32/disp8 -2482 $emit-modrm:rm32: -2483 # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) -2484 # . EAX = parse-datum-of-word(word-slice) -2485 # . . push args -2486 51/push-ECX -2487 # . . call -2488 e8/call parse-datum-of-word/disp32 -2489 # . . discard args -2490 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2491 # . rm32 = EAX -2492 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI -2493 # has-modrm? = true -2494 ba/copy-to-EDX 1/imm32/true -2495 # continue -2496 e9/jump $emit-modrm:loop/disp32 -2497 $emit-modrm:check-for-r32: -2498 # if (has-metadata?(word-slice, "r32")) -2499 # . EAX = has-metadata?(ECX, "r32") -2500 # . . push args -2501 68/push "r32"/imm32 -2502 51/push-ECX -2503 # . . call -2504 e8/call has-metadata?/disp32 -2505 # . . discard args -2506 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2507 # . if (EAX == 0) goto next check -2508 3d/compare-EAX-and 0/imm32 -2509 74/jump-if-equal $emit-modrm:check-for-subop/disp8 -2510 $emit-modrm:r32: -2511 # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) -2512 # . EAX = parse-datum-of-word(word-slice) -2513 # . . push args -2514 51/push-ECX -2515 # . . call -2516 e8/call parse-datum-of-word/disp32 -2517 # . . discard args -2518 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2519 # . r32 = EAX -2520 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI -2521 # has-modrm? = true -2522 ba/copy-to-EDX 1/imm32/true -2523 # continue -2524 e9/jump $emit-modrm:loop/disp32 -2525 $emit-modrm:check-for-subop: -2526 # if (has-metadata?(word-slice, "subop")) -2527 # . EAX = has-metadata?(ECX, "subop") -2528 # . . push args -2529 68/push "subop"/imm32 -2530 51/push-ECX -2531 # . . call -2532 e8/call has-metadata?/disp32 -2533 # . . discard args -2534 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2535 # . if (EAX == 0) loop -2536 3d/compare-EAX-and 0/imm32 -2537 0f 84/jump-if-equal $emit-modrm:loop/disp32 -2538 $emit-modrm:subop: -2539 # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) -2540 # . EAX = parse-datum-of-word(word-slice) -2541 # . . push args -2542 51/push-ECX -2543 # . . call -2544 e8/call parse-datum-of-word/disp32 -2545 # . . discard args -2546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2547 # . r32 = EAX -2548 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI -2549 # has-modrm? = true -2550 ba/copy-to-EDX 1/imm32/true -2551 # continue -2552 e9/jump $emit-modrm:loop/disp32 -2553 $emit-modrm:break: -2554 # if (!has-modrm?) return -2555 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX -2556 74/jump-if-equal $emit-modrm:end/disp8 -2557 $emit-modrm:calculate: -2558 # modrm/EBX = mod & 0b11 -2559 81 4/subop/and 3/mod/direct 3/rm32/EBX . . . . . 3/imm32/0b11 # bitwise and of EBX -2560 # modrm <<= 2 -2561 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 2/imm8 # shift EBX left by 2 bits -2562 # modrm |= r32 & 0b111 -2563 81 4/subop/and 3/mod/direct 7/rm32/EDI . . . . . 7/imm32/0b111 # bitwise and of EDI -2564 09/or 3/mod/direct 3/rm32/EBX . . . 7/r32/EDI . . # EBX = bitwise OR with EDI -2565 # modrm <<= 3 -2566 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 3/imm8 # shift EBX left by 3 bits -2567 # modrm |= rm32 & 0b111 -2568 81 4/subop/and 3/mod/direct 6/rm32/ESI . . . . . 7/imm32/0b111 # bitwise and of ESI -2569 09/or 3/mod/direct 3/rm32/EBX . . . 6/r32/ESI . . # EBX = bitwise OR with ESI -2570 $emit-modrm:emit: -2571 # emit-hex(out, modrm, 1) -2572 # . . push args -2573 68/push 1/imm32 -2574 53/push-EBX -2575 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2576 # . . call -2577 e8/call emit-hex/disp32 -2578 # . . discard args -2579 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2580 $emit-modrm:end: -2581 # . restore locals -2582 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2583 # . restore registers -2584 5f/pop-to-EDI -2585 5e/pop-to-ESI -2586 5b/pop-to-EBX -2587 5a/pop-to-EDX -2588 59/pop-to-ECX -2589 58/pop-to-EAX -2590 # . epilog -2591 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2592 5d/pop-to-EBP -2593 c3/return -2594 -2595 emit-sib: # line : (address stream byte), out : (address buffered-file) -> <void> -2596 # pseudocode: -2597 # var has-sib? = false, base = 0, index = 0, scale = 0 -2598 # var word-slice = {0, 0} -2599 # while true -2600 # word-slice = next-word(line) -2601 # if (slice-empty?(word-slice)) break -2602 # if (slice-starts-with?(word-slice, "#")) break -2603 # if (has-metadata?(word-slice, "base") -2604 # base = parse-hex-int(next-token-from-slice(word-slice, "/")) -2605 # has-sib? = true -2606 # else if (has-metadata?(word-slice, "index") -2607 # index = parse-hex-int(next-token-from-slice(word-slice, "/")) -2608 # has-sib? = true -2609 # else if (has-metadata?(word-slice, "scale") -2610 # scale = parse-hex-int(next-token-from-slice(word-slice, "/")) -2611 # has-sib? = true -2612 # if has-sib? -2613 # var sib = scale & 0b11 -2614 # sib <<= 2 -2615 # sib |= index & 0b111 -2616 # sib <<= 3 -2617 # sib |= base & 0b111 -2618 # emit-hex(out, sib, 1) -2619 # -2620 # . prolog -2621 55/push-EBP -2622 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2623 # . save registers -2624 50/push-EAX -2625 51/push-ECX -2626 52/push-EDX -2627 53/push-EBX -2628 56/push-ESI -2629 57/push-EDI -2630 # var word-slice/ECX = {0, 0} -2631 68/push 0/imm32/end -2632 68/push 0/imm32/start -2633 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -2634 # var has-sib?/EDX = false -2635 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX -2636 # var scale/EBX = 0 -2637 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -2638 # var base/ESI = 0 -2639 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI -2640 # var index/EDI = 0 -2641 31/xor 3/mod/direct 7/rm32/EDI . . . 7/r32/EDI . . # clear EDI -2642 # rewind-stream(line) -2643 # . . push args -2644 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2645 # . . call -2646 e8/call rewind-stream/disp32 -2647 # . . discard args -2648 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2649 $emit-sib:loop: -2650 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- -2676 # next-word(line, word-slice) -2677 # . . push args -2678 51/push-ECX -2679 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2680 # . . call -2681 e8/call next-word/disp32 -2682 # . . discard args -2683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2684 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- -2726 $emit-sib:check0: -2727 # if (slice-empty?(word-slice)) break -2728 # . EAX = slice-empty?(word-slice) -2729 # . . push args -2730 51/push-ECX -2731 # . . call -2732 e8/call slice-empty?/disp32 -2733 # . . discard args -2734 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2735 # . if (EAX != 0) pass through -2736 3d/compare-EAX-and 0/imm32 -2737 0f 85/jump-if-not-equal $emit-sib:break/disp32 -2738 $emit-sib:check1: -2739 # if (slice-starts-with?(word-slice, "#")) break -2740 # . spill EDX -2741 52/push-EDX -2742 # . start/EDX = word-slice->start -2743 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX -2744 # . c/EAX = *start -2745 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2746 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL -2747 # . restore EDX -2748 5a/pop-to-EDX -2749 # . if (EAX == '#') pass through -2750 3d/compare-EAX-and 0x23/imm32/hash -2751 0f 84/jump-if-equal $emit-sib:break/disp32 -2752 $emit-sib:check-for-scale: -2753 # if (has-metadata?(word-slice, "scale")) -2754 # . EAX = has-metadata?(ECX, "scale") -2755 # . . push args -2756 68/push "scale"/imm32 -2757 51/push-ECX -2758 # . . call -2759 e8/call has-metadata?/disp32 -2760 # . . discard args -2761 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2762 # . if (EAX == 0) goto next check -2763 3d/compare-EAX-and 0/imm32 -2764 74/jump-if-equal $emit-sib:check-for-base/disp8 -2765 $emit-sib:scale: -2766 # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) -2767 # . EAX = parse-datum-of-word(word-slice) -2768 # . . push args -2769 51/push-ECX -2770 # . . call -2771 e8/call parse-datum-of-word/disp32 -2772 # . . discard args -2773 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2774 # . scale = EAX -2775 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX -2776 # has-sib? = true -2777 ba/copy-to-EDX 1/imm32/true -2778 # continue -2779 e9/jump $emit-sib:loop/disp32 -2780 $emit-sib:check-for-base: -2781 # if (has-metadata?(word-slice, "base")) -2782 # . EAX = has-metadata?(ECX, "base") -2783 # . . push args -2784 68/push "base"/imm32 -2785 51/push-ECX -2786 # . . call -2787 e8/call has-metadata?/disp32 -2788 # . . discard args -2789 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2790 # . if (EAX == 0) goto next check -2791 3d/compare-EAX-and 0/imm32 -2792 74/jump-if-equal $emit-sib:check-for-index/disp8 -2793 $emit-sib:base: -2794 # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) -2795 # . EAX = parse-datum-of-word(word-slice) -2796 # . . push args -2797 51/push-ECX -2798 # . . call -2799 e8/call parse-datum-of-word/disp32 -2800 # . . discard args -2801 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2802 # . base = EAX -2803 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI -2804 # has-sib? = true -2805 ba/copy-to-EDX 1/imm32/true -2806 # continue -2807 e9/jump $emit-sib:loop/disp32 -2808 $emit-sib:check-for-index: -2809 # if (has-metadata?(word-slice, "index")) -2810 # . EAX = has-metadata?(ECX, "index") -2811 # . . push args -2812 68/push "index"/imm32 -2813 51/push-ECX -2814 # . . call -2815 e8/call has-metadata?/disp32 -2816 # . . discard args -2817 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2818 # . if (EAX == 0) loop -2819 3d/compare-EAX-and 0/imm32 -2820 0f 84/jump-if-equal $emit-sib:loop/disp32 -2821 $emit-sib:index: -2822 # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) -2823 # . EAX = parse-datum-of-word(word-slice) -2824 # . . push args -2825 51/push-ECX -2826 # . . call -2827 e8/call parse-datum-of-word/disp32 -2828 # . . discard args -2829 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2830 # . index = EAX -2831 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI -2832 # has-sib? = true -2833 ba/copy-to-EDX 1/imm32/true -2834 # continue -2835 e9/jump $emit-sib:loop/disp32 -2836 $emit-sib:break: -2837 # if (!has-sib?) return -2838 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX -2839 74/jump-if-equal $emit-sib:end/disp8 -2840 $emit-sib:calculate: -2841 # sib/EBX = scale & 0b11 -2842 81 4/subop/and 3/mod/direct 3/rm32/EBX . . . . . 3/imm32/0b11 # bitwise and of EBX -2843 # sib <<= 2 -2844 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 2/imm8 # shift EBX left by 2 bits -2845 # sib |= index & 0b111 -2846 81 4/subop/and 3/mod/direct 7/rm32/EDI . . . . . 7/imm32/0b111 # bitwise and of EDI -2847 09/or 3/mod/direct 3/rm32/EBX . . . 7/r32/EDI . . # EBX = bitwise OR with EDI -2848 # sib <<= 3 -2849 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 3/imm8 # shift EBX left by 3 bits -2850 # sib |= base & 0b111 -2851 81 4/subop/and 3/mod/direct 6/rm32/ESI . . . . . 7/imm32/0b111 # bitwise and of ESI -2852 09/or 3/mod/direct 3/rm32/EBX . . . 6/r32/ESI . . # EBX = bitwise OR with ESI -2853 $emit-sib:emit: -2854 # emit-hex(out, sib, 1) -2855 # . . push args -2856 68/push 1/imm32 -2857 53/push-EBX -2858 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -2859 # . . call -2860 e8/call emit-hex/disp32 -2861 # . . discard args -2862 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2863 $emit-sib:end: -2864 # . restore locals -2865 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2866 # . restore registers -2867 5f/pop-to-EDI -2868 5e/pop-to-ESI -2869 5b/pop-to-EBX -2870 5a/pop-to-EDX -2871 59/pop-to-ECX -2872 58/pop-to-EAX -2873 # . epilog -2874 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2875 5d/pop-to-EBP -2876 c3/return -2877 -2878 emit-disp: # line : (address stream byte), out : (address buffered-file) -> <void> -2879 # pseudocode: -2880 # rewind-stream(line) -2881 # var word-slice = {0, 0} -2882 # while true -2883 # word-slice = next-word(line) -2884 # if (slice-empty?(word-slice)) break -2885 # if (slice-starts-with?(word-slice, "#")) break -2886 # if has-metadata?(word-slice, "disp32") -2887 # emit(out, word-slice, 4) -2888 # break -2889 # if has-metadata?(word-slice, "disp16") -2890 # emit(out, word-slice, 2) -2891 # break -2892 # if has-metadata?(word-slice, "disp8") -2893 # emit(out, word-slice, 1) -2894 # break -2895 # -2896 # . prolog -2897 55/push-EBP -2898 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2899 # . save registers -2900 50/push-EAX -2901 51/push-ECX -2902 52/push-EDX -2903 # var word-slice/ECX = {0, 0} -2904 68/push 0/imm32/end -2905 68/push 0/imm32/start -2906 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -2907 # rewind-stream(line) -2908 # . . push args -2909 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2910 # . . call -2911 e8/call rewind-stream/disp32 -2912 # . . discard args -2913 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2914 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- -2940 $emit-disp:loop: -2941 # next-word(line, word-slice) -2942 # . . push args -2943 51/push-ECX -2944 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -2945 # . . call -2946 e8/call next-word/disp32 -2947 # . . discard args -2948 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2949 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- -2991 $emit-disp:check0: -2992 # if (slice-empty?(word-slice)) break -2993 # . EAX = slice-empty?(word-slice) -2994 # . . push args -2995 51/push-ECX -2996 # . . call -2997 e8/call slice-empty?/disp32 -2998 # . . discard args -2999 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3000 # . if (EAX != 0) pass through -3001 3d/compare-EAX-and 0/imm32 -3002 0f 85/jump-if-not-equal $emit-disp:break/disp32 -3003 $emit-disp:check1: -3004 # if (slice-starts-with?(word-slice, "#")) break -3005 # . start/EDX = word-slice->start -3006 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX -3007 # . c/EAX = *start -3008 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -3009 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL -3010 # . if (EAX == '#') break -3011 3d/compare-EAX-and 0x23/imm32/hash -3012 0f 84/jump-if-equal $emit-disp:break/disp32 -3013 $emit-disp:check-for-disp32: -3014 # if (has-metadata?(word-slice, "disp32")) -3015 # . EAX = has-metadata?(ECX, "disp32") -3016 # . . push args -3017 68/push "disp32"/imm32 -3018 51/push-ECX -3019 # . . call -3020 e8/call has-metadata?/disp32 -3021 # . . discard args -3022 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3023 # . if (EAX == 0) goto next check -3024 3d/compare-EAX-and 0/imm32 -3025 74/jump-if-equal $emit-disp:check-for-disp16/disp8 -3026 $emit-disp:disp32: -3027 # emit(out, word-slice, 4) -3028 # . . push args -3029 68/push 4/imm32 -3030 51/push-ECX -3031 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3032 # . . call -3033 e8/call emit/disp32 -3034 # . . discard args -3035 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3036 # break -3037 e9/jump $emit-disp:break/disp32 -3038 $emit-disp:check-for-disp16: -3039 # else if (has-metadata?(word-slice, "disp16")) -3040 # . EAX = has-metadata?(ECX, "disp16") -3041 # . . push args -3042 68/push "disp16"/imm32 -3043 51/push-ECX -3044 # . . call -3045 e8/call has-metadata?/disp32 -3046 # . . discard args -3047 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3048 # . if (EAX == 0) goto next check -3049 3d/compare-EAX-and 0/imm32 -3050 74/jump-if-equal $emit-disp:check-for-disp8/disp8 -3051 $emit-disp:disp16: -3052 # emit(out, word-slice, 2) -3053 # . . push args -3054 68/push 2/imm32 -3055 51/push-ECX -3056 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3057 # . . call -3058 e8/call emit/disp32 -3059 # . . discard args -3060 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3061 # break -3062 e9/jump $emit-disp:break/disp32 -3063 $emit-disp:check-for-disp8: -3064 # if (has-metadata?(word-slice, "disp8")) -3065 # . EAX = has-metadata?(ECX, "disp8") -3066 # . . push args -3067 68/push "disp8"/imm32 -3068 51/push-ECX -3069 # . . call -3070 e8/call has-metadata?/disp32 -3071 # . . discard args -3072 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3073 # . if (EAX == 0) loop -3074 3d/compare-EAX-and 0/imm32 -3075 0f 84/jump-if-equal $emit-disp:loop/disp32 -3076 $emit-disp:disp8: -3077 # emit(out, word-slice, 1) -3078 # . . push args -3079 68/push 1/imm32 -3080 51/push-ECX -3081 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3082 # . . call -3083 e8/call emit/disp32 -3084 # . . discard args -3085 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3086 # break -3087 $emit-disp:break: -3088 # . restore locals -3089 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3090 # . restore registers -3091 5a/pop-to-EDX -3092 59/pop-to-ECX -3093 58/pop-to-EAX -3094 # . epilog -3095 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3096 5d/pop-to-EBP -3097 c3/return -3098 -3099 emit-imm: # line : (address stream byte), out : (address buffered-file) -> <void> -3100 # pseudocode: -3101 # rewind-stream(line) -3102 # var word-slice = {0, 0} -3103 # while true -3104 # word-slice = next-word(line) -3105 # if (slice-empty?(word-slice)) break -3106 # if (slice-starts-with?(word-slice, "#")) break -3107 # if has-metadata?(word-slice, "imm32") -3108 # emit(out, word-slice, 4) -3109 # break -3110 # if has-metadata?(word-slice, "imm16") -3111 # emit(out, word-slice, 2) -3112 # break -3113 # if has-metadata?(word-slice, "imm8") -3114 # emit(out, word-slice, 1) -3115 # break -3116 # -3117 # . prolog -3118 55/push-EBP -3119 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3120 # . save registers -3121 50/push-EAX -3122 51/push-ECX -3123 52/push-EDX -3124 # var word-slice/ECX = {0, 0} -3125 68/push 0/imm32/end -3126 68/push 0/imm32/start -3127 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -3128 # rewind-stream(line) -3129 # . . push args -3130 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -3131 # . . call -3132 e8/call rewind-stream/disp32 -3133 # . . discard args -3134 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3135 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- -3161 $emit-imm:loop: -3162 # next-word(line, word-slice) -3163 # . . push args -3164 51/push-ECX -3165 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -3166 # . . call -3167 e8/call next-word/disp32 -3168 # . . discard args -3169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3170 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- -3212 $emit-imm:check0: -3213 # if (slice-empty?(word-slice)) break -3214 # . EAX = slice-empty?(word-slice) -3215 # . . push args -3216 51/push-ECX -3217 # . . call -3218 e8/call slice-empty?/disp32 -3219 # . . discard args -3220 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3221 # . if (EAX != 0) pass through -3222 3d/compare-EAX-and 0/imm32 -3223 0f 85/jump-if-not-equal $emit-imm:break/disp32 -3224 $emit-imm:check1: -3225 # if (slice-starts-with?(word-slice, "#")) break -3226 # . start/EDX = slice->start -3227 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX -3228 # . c/EAX = *start -3229 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -3230 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL -3231 # . if (EAX == '#') break -3232 3d/compare-EAX-and 0x23/imm32/hash -3233 0f 84/jump-if-equal $emit-imm:break/disp32 -3234 $emit-imm:check-for-imm32: -3235 # if (has-metadata?(word-slice, "imm32")) -3236 # . EAX = has-metadata?(ECX, "imm32") -3237 # . . push args -3238 68/push "imm32"/imm32 -3239 51/push-ECX -3240 # . . call -3241 e8/call has-metadata?/disp32 -3242 # . . discard args -3243 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3244 # . if (EAX == 0) goto next check -3245 3d/compare-EAX-and 0/imm32 -3246 74/jump-if-equal $emit-imm:check-for-imm16/disp8 -3247 $emit-imm:imm32: -3248 # emit(out, word-slice, 4) -3249 # . . push args -3250 68/push 4/imm32 -3251 51/push-ECX -3252 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3253 # . . call -3254 e8/call emit/disp32 -3255 # . . discard args -3256 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3257 # break -3258 e9/jump $emit-imm:break/disp32 -3259 $emit-imm:check-for-imm16: -3260 # if (has-metadata?(word-slice, "imm16")) -3261 # . EAX = has-metadata?(ECX, "imm16") -3262 # . . push args -3263 68/push "imm16"/imm32 -3264 51/push-ECX -3265 # . . call -3266 e8/call has-metadata?/disp32 -3267 # . . discard args -3268 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3269 # . if (EAX == 0) goto next check -3270 3d/compare-EAX-and 0/imm32 -3271 74/jump-if-equal $emit-imm:check-for-imm8/disp8 -3272 $emit-imm:imm16: -3273 # emit(out, word-slice, 2) -3274 # . . push args -3275 68/push 2/imm32 -3276 51/push-ECX -3277 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3278 # . . call -3279 e8/call emit/disp32 -3280 # . . discard args -3281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3282 # break -3283 e9/jump $emit-imm:break/disp32 -3284 $emit-imm:check-for-imm8: -3285 # if (has-metadata?(word-slice, "imm8")) -3286 # . EAX = has-metadata?(ECX, "imm8") -3287 # . . push args -3288 68/push "imm8"/imm32 -3289 51/push-ECX -3290 # . . call -3291 e8/call has-metadata?/disp32 -3292 # . . discard args -3293 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3294 # . if (EAX == 0) loop -3295 3d/compare-EAX-and 0/imm32 -3296 0f 84/jump-if-equal $emit-imm:loop/disp32 -3297 $emit-imm:imm8: -3298 # emit(out, word-slice, 1) -3299 # . . push args -3300 68/push 1/imm32 -3301 51/push-ECX -3302 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3303 # . . call -3304 e8/call emit/disp32 -3305 # . . discard args -3306 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3307 # break -3308 $emit-imm:break: -3309 # . restore locals -3310 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3311 # . restore registers -3312 5a/pop-to-EDX -3313 59/pop-to-ECX -3314 58/pop-to-EAX -3315 # . epilog -3316 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3317 5d/pop-to-EBP -3318 c3/return -3319 -3320 emit-line-in-comment: # line : (address stream byte), out : (address buffered-file) -> <void> -3321 # . prolog -3322 55/push-EBP -3323 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3324 # write-buffered(out, " # ") -3325 # . . push args -3326 68/push " # "/imm32 -3327 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3328 # . . call -3329 e8/call write-buffered/disp32 -3330 # . . discard args -3331 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3332 # write-stream-data(out, line) -3333 # . . push args -3334 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -3335 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -3336 # . . call -3337 e8/call write-stream-data/disp32 -3338 # . . discard args -3339 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3340 $emit-line-in-comment:end: -3341 # . epilog -3342 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3343 5d/pop-to-EBP -3344 c3/return -3345 -3346 test-convert-instruction-passes-comments-through: -3347 # if a line starts with '#', pass it along unchanged -3348 # . prolog -3349 55/push-EBP -3350 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3351 # setup -3352 # . clear-stream(_test-input-stream) -3353 # . . push args -3354 68/push _test-input-stream/imm32 -3355 # . . call -3356 e8/call clear-stream/disp32 -3357 # . . discard args -3358 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3359 # . clear-stream(_test-output-stream) -3360 # . . push args -3361 68/push _test-output-stream/imm32 -3362 # . . call -3363 e8/call clear-stream/disp32 -3364 # . . discard args -3365 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3366 # . clear-stream(_test-output-buffered-file+4) -3367 # . . push args -3368 b8/copy-to-EAX _test-output-buffered-file/imm32 -3369 05/add-to-EAX 4/imm32 -3370 50/push-EAX -3371 # . . call -3372 e8/call clear-stream/disp32 -3373 # . . discard args -3374 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3375 # initialize input -3376 # . write(_test-input-stream, "# abcd") -3377 # . . push args -3378 68/push "# abcd"/imm32 -3379 68/push _test-input-stream/imm32 -3380 # . . call -3381 e8/call write/disp32 -3382 # . . discard args -3383 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3384 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3385 # . . push args -3386 68/push _test-output-buffered-file/imm32 -3387 68/push _test-input-stream/imm32 -3388 # . . call -3389 e8/call convert-instruction/disp32 -3390 # . . discard args -3391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3392 # check that the line just passed through -3393 # . flush(_test-output-buffered-file) -3394 # . . push args -3395 68/push _test-output-buffered-file/imm32 -3396 # . . call -3397 e8/call flush/disp32 -3398 # . . discard args -3399 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3400 # . check-stream-equal(_test-output-stream, "# abcd", msg) -3401 # . . push args -3402 68/push "F - test-convert-instruction-passes-comments-through"/imm32 -3403 68/push "# abcd"/imm32 -3404 68/push _test-output-stream/imm32 -3405 # . . call -3406 e8/call check-stream-equal/disp32 -3407 # . . discard args -3408 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3409 # . epilog -3410 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3411 5d/pop-to-EBP -3412 c3/return -3413 -3414 test-convert-instruction-passes-labels-through: -3415 # if the first word ends with ':', pass along the entire line unchanged -3416 # . prolog -3417 55/push-EBP -3418 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3419 # setup -3420 # . clear-stream(_test-input-stream) -3421 # . . push args -3422 68/push _test-input-stream/imm32 -3423 # . . call -3424 e8/call clear-stream/disp32 -3425 # . . discard args -3426 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3427 # . clear-stream(_test-output-stream) -3428 # . . push args -3429 68/push _test-output-stream/imm32 -3430 # . . call -3431 e8/call clear-stream/disp32 -3432 # . . discard args -3433 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3434 # . clear-stream(_test-output-buffered-file+4) -3435 # . . push args -3436 b8/copy-to-EAX _test-output-buffered-file/imm32 -3437 05/add-to-EAX 4/imm32 -3438 50/push-EAX -3439 # . . call -3440 e8/call clear-stream/disp32 -3441 # . . discard args -3442 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3443 # initialize input -3444 # . write(_test-input-stream, "ab: # cd") -3445 # . . push args -3446 68/push "ab: # cd"/imm32 -3447 68/push _test-input-stream/imm32 -3448 # . . call -3449 e8/call write/disp32 -3450 # . . discard args -3451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3452 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3453 # . . push args -3454 68/push _test-output-buffered-file/imm32 -3455 68/push _test-input-stream/imm32 -3456 # . . call -3457 e8/call convert-instruction/disp32 -3458 # . . discard args -3459 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3460 # check that the line just passed through -3461 # . flush(_test-output-buffered-file) -3462 # . . push args -3463 68/push _test-output-buffered-file/imm32 -3464 # . . call -3465 e8/call flush/disp32 -3466 # . . discard args -3467 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3468 # . check-stream-equal(_test-output-stream, "ab: # cd", msg) -3469 # . . push args -3470 68/push "F - test-convert-instruction-passes-labels-through"/imm32 -3471 68/push "ab: # cd"/imm32 -3472 68/push _test-output-stream/imm32 -3473 # . . call -3474 e8/call check-stream-equal/disp32 -3475 # . . discard args -3476 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3477 # . epilog -3478 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3479 5d/pop-to-EBP -3480 c3/return -3481 -3482 test-convert-instruction-handles-single-opcode: -3483 # if the instruction consists of a single opcode, strip its metadata and pass it along -3484 # . prolog -3485 55/push-EBP -3486 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3487 # setup -3488 # . clear-stream(_test-input-stream) -3489 # . . push args -3490 68/push _test-input-stream/imm32 -3491 # . . call -3492 e8/call clear-stream/disp32 -3493 # . . discard args -3494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3495 # . clear-stream(_test-output-stream) -3496 # . . push args -3497 68/push _test-output-stream/imm32 -3498 # . . call -3499 e8/call clear-stream/disp32 -3500 # . . discard args -3501 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3502 # . clear-stream(_test-output-buffered-file+4) -3503 # . . push args -3504 b8/copy-to-EAX _test-output-buffered-file/imm32 -3505 05/add-to-EAX 4/imm32 -3506 50/push-EAX -3507 # . . call -3508 e8/call clear-stream/disp32 -3509 # . . discard args -3510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3511 # initialize input -3512 # . write(_test-input-stream, "ab/cd # comment") -3513 # . . push args -3514 68/push "ab/cd # comment"/imm32 -3515 68/push _test-input-stream/imm32 -3516 # . . call -3517 e8/call write/disp32 -3518 # . . discard args -3519 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3520 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3521 # . . push args -3522 68/push _test-output-buffered-file/imm32 -3523 68/push _test-input-stream/imm32 -3524 # . . call -3525 e8/call convert-instruction/disp32 -3526 # . . discard args -3527 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3528 # check output -3529 # . flush(_test-output-buffered-file) -3530 # . . push args -3531 68/push _test-output-buffered-file/imm32 -3532 # . . call -3533 e8/call flush/disp32 -3534 # . . discard args -3535 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3536 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -3562 # . check-stream-equal(_test-output-stream, "ab # ab/cd # comment", msg) -3563 # . . push args -3564 68/push "F - test-convert-instruction-handles-single-opcode"/imm32 -3565 68/push "ab # ab/cd # comment"/imm32 -3566 68/push _test-output-stream/imm32 -3567 # . . call -3568 e8/call check-stream-equal/disp32 -3569 # . . discard args -3570 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3571 # . epilog -3572 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3573 5d/pop-to-EBP -3574 c3/return -3575 -3576 test-convert-instruction-handles-0f-opcode: -3577 # if the instruction starts with 0f opcode, include a second opcode -3578 # . prolog -3579 55/push-EBP -3580 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3581 # setup -3582 # . clear-stream(_test-input-stream) -3583 # . . push args -3584 68/push _test-input-stream/imm32 -3585 # . . call -3586 e8/call clear-stream/disp32 -3587 # . . discard args -3588 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3589 # . clear-stream(_test-output-stream) -3590 # . . push args -3591 68/push _test-output-stream/imm32 -3592 # . . call -3593 e8/call clear-stream/disp32 -3594 # . . discard args -3595 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3596 # . clear-stream(_test-output-buffered-file+4) -3597 # . . push args -3598 b8/copy-to-EAX _test-output-buffered-file/imm32 -3599 05/add-to-EAX 4/imm32 -3600 50/push-EAX -3601 # . . call -3602 e8/call clear-stream/disp32 -3603 # . . discard args -3604 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3605 # initialize input -3606 # . write(_test-input-stream, "0f/m1 ab/m2 # comment") -3607 # . . push args -3608 68/push "0f/m1 ab/m2 # comment"/imm32 -3609 68/push _test-input-stream/imm32 -3610 # . . call -3611 e8/call write/disp32 -3612 # . . discard args -3613 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3614 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3615 # . . push args -3616 68/push _test-output-buffered-file/imm32 -3617 68/push _test-input-stream/imm32 -3618 # . . call -3619 e8/call convert-instruction/disp32 -3620 # . . discard args -3621 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3622 # check output -3623 # . flush(_test-output-buffered-file) -3624 # . . push args -3625 68/push _test-output-buffered-file/imm32 -3626 # . . call -3627 e8/call flush/disp32 -3628 # . . discard args -3629 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3630 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -3656 # . check-stream-equal(_test-output-stream, "0f ab # 0f/m1 ab/m2 # comment", msg) -3657 # . . push args -3658 68/push "F - test-convert-instruction-handles-0f-opcode"/imm32 -3659 68/push "0f ab # 0f/m1 ab/m2 # comment"/imm32 -3660 68/push _test-output-stream/imm32 -3661 # . . call -3662 e8/call check-stream-equal/disp32 -3663 # . . discard args -3664 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3665 # . epilog -3666 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3667 5d/pop-to-EBP -3668 c3/return -3669 -3670 test-convert-instruction-handles-f2-opcode: -3671 # if the instruction starts with f2 opcode, include a second opcode -3672 # . prolog -3673 55/push-EBP -3674 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3675 # setup -3676 # . clear-stream(_test-input-stream) -3677 # . . push args -3678 68/push _test-input-stream/imm32 -3679 # . . call -3680 e8/call clear-stream/disp32 -3681 # . . discard args -3682 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3683 # . clear-stream(_test-output-stream) -3684 # . . push args -3685 68/push _test-output-stream/imm32 -3686 # . . call -3687 e8/call clear-stream/disp32 -3688 # . . discard args -3689 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3690 # . clear-stream(_test-output-buffered-file+4) -3691 # . . push args -3692 b8/copy-to-EAX _test-output-buffered-file/imm32 -3693 05/add-to-EAX 4/imm32 -3694 50/push-EAX -3695 # . . call -3696 e8/call clear-stream/disp32 -3697 # . . discard args -3698 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3699 # initialize input -3700 # . write(_test-input-stream, "f2/m1 ab/m2 # comment") -3701 # . . push args -3702 68/push "f2/m1 ab/m2 # comment"/imm32 -3703 68/push _test-input-stream/imm32 -3704 # . . call -3705 e8/call write/disp32 -3706 # . . discard args -3707 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3708 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3709 # . . push args -3710 68/push _test-output-buffered-file/imm32 -3711 68/push _test-input-stream/imm32 -3712 # . . call -3713 e8/call convert-instruction/disp32 -3714 # . . discard args -3715 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3716 # check output -3717 # . flush(_test-output-buffered-file) -3718 # . . push args -3719 68/push _test-output-buffered-file/imm32 -3720 # . . call -3721 e8/call flush/disp32 -3722 # . . discard args -3723 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3724 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -3750 # . check-stream-equal(_test-output-stream, "f2 ab # f2/m1 ab/m2 # comment", msg) -3751 # . . push args -3752 68/push "F - test-convert-instruction-handles-f2-opcode"/imm32 -3753 68/push "f2 ab # f2/m1 ab/m2 # comment"/imm32 -3754 68/push _test-output-stream/imm32 -3755 # . . call -3756 e8/call check-stream-equal/disp32 -3757 # . . discard args -3758 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3759 # . epilog -3760 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3761 5d/pop-to-EBP -3762 c3/return -3763 -3764 test-convert-instruction-handles-f3-opcode: -3765 # if the instruction starts with f3 opcode, include a second opcode -3766 # . prolog -3767 55/push-EBP -3768 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3769 # setup -3770 # . clear-stream(_test-input-stream) -3771 # . . push args -3772 68/push _test-input-stream/imm32 -3773 # . . call -3774 e8/call clear-stream/disp32 -3775 # . . discard args -3776 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3777 # . clear-stream(_test-output-stream) -3778 # . . push args -3779 68/push _test-output-stream/imm32 -3780 # . . call -3781 e8/call clear-stream/disp32 -3782 # . . discard args -3783 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3784 # . clear-stream(_test-output-buffered-file+4) -3785 # . . push args -3786 b8/copy-to-EAX _test-output-buffered-file/imm32 -3787 05/add-to-EAX 4/imm32 -3788 50/push-EAX -3789 # . . call -3790 e8/call clear-stream/disp32 -3791 # . . discard args -3792 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3793 # initialize input -3794 # . write(_test-input-stream, "f3/m1 ab/m2 # comment") -3795 # . . push args -3796 68/push "f3/m1 ab/m2 # comment"/imm32 -3797 68/push _test-input-stream/imm32 -3798 # . . call -3799 e8/call write/disp32 -3800 # . . discard args -3801 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3802 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3803 # . . push args -3804 68/push _test-output-buffered-file/imm32 -3805 68/push _test-input-stream/imm32 -3806 # . . call -3807 e8/call convert-instruction/disp32 -3808 # . . discard args -3809 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3810 # check output -3811 # . flush(_test-output-buffered-file) -3812 # . . push args -3813 68/push _test-output-buffered-file/imm32 -3814 # . . call -3815 e8/call flush/disp32 -3816 # . . discard args -3817 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3818 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -3844 # . check-stream-equal(_test-output-stream, "f3 ab # f3/m1 ab/m2 # comment", msg) -3845 # . . push args -3846 68/push "F - test-convert-instruction-handles-f3-opcode"/imm32 -3847 68/push "f3 ab # f3/m1 ab/m2 # comment"/imm32 -3848 68/push _test-output-stream/imm32 -3849 # . . call -3850 e8/call check-stream-equal/disp32 -3851 # . . discard args -3852 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3853 # . epilog -3854 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3855 5d/pop-to-EBP -3856 c3/return -3857 -3858 test-convert-instruction-handles-f2-0f-opcode: -3859 # if the instruction starts with f2 0f opcode, include a second opcode -3860 # . prolog -3861 55/push-EBP -3862 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3863 # setup -3864 # . clear-stream(_test-input-stream) -3865 # . . push args -3866 68/push _test-input-stream/imm32 -3867 # . . call -3868 e8/call clear-stream/disp32 -3869 # . . discard args -3870 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3871 # . clear-stream(_test-output-stream) -3872 # . . push args -3873 68/push _test-output-stream/imm32 -3874 # . . call -3875 e8/call clear-stream/disp32 -3876 # . . discard args -3877 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3878 # . clear-stream(_test-output-buffered-file+4) -3879 # . . push args -3880 b8/copy-to-EAX _test-output-buffered-file/imm32 -3881 05/add-to-EAX 4/imm32 -3882 50/push-EAX -3883 # . . call -3884 e8/call clear-stream/disp32 -3885 # . . discard args -3886 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3887 # initialize input -3888 # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment") -3889 # . . push args -3890 68/push "f2/m1 0f/m2 ab/m3 # comment"/imm32 -3891 68/push _test-input-stream/imm32 -3892 # . . call -3893 e8/call write/disp32 -3894 # . . discard args -3895 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3896 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3897 # . . push args -3898 68/push _test-output-buffered-file/imm32 -3899 68/push _test-input-stream/imm32 -3900 # . . call -3901 e8/call convert-instruction/disp32 -3902 # . . discard args -3903 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3904 # check output -3905 # . flush(_test-output-buffered-file) -3906 # . . push args -3907 68/push _test-output-buffered-file/imm32 -3908 # . . call -3909 e8/call flush/disp32 -3910 # . . discard args -3911 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3912 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -3938 # . check-stream-equal(_test-output-stream, "f2 0f ab # f2/m1 0f/m2 ab/m3 # comment", msg) -3939 # . . push args -3940 68/push "F - test-convert-instruction-handles-f2-0f-opcode"/imm32 -3941 68/push "f2 0f ab # f2/m1 0f/m2 ab/m3 # comment"/imm32 -3942 68/push _test-output-stream/imm32 -3943 # . . call -3944 e8/call check-stream-equal/disp32 -3945 # . . discard args -3946 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -3947 # . epilog -3948 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -3949 5d/pop-to-EBP -3950 c3/return -3951 -3952 test-convert-instruction-handles-f3-0f-opcode: -3953 # if the instruction starts with f3 0f opcode, include a second opcode -3954 # . prolog -3955 55/push-EBP -3956 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -3957 # setup -3958 # . clear-stream(_test-input-stream) -3959 # . . push args -3960 68/push _test-input-stream/imm32 -3961 # . . call -3962 e8/call clear-stream/disp32 -3963 # . . discard args -3964 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3965 # . clear-stream(_test-output-stream) -3966 # . . push args -3967 68/push _test-output-stream/imm32 -3968 # . . call -3969 e8/call clear-stream/disp32 -3970 # . . discard args -3971 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3972 # . clear-stream(_test-output-buffered-file+4) -3973 # . . push args -3974 b8/copy-to-EAX _test-output-buffered-file/imm32 -3975 05/add-to-EAX 4/imm32 -3976 50/push-EAX -3977 # . . call -3978 e8/call clear-stream/disp32 -3979 # . . discard args -3980 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -3981 # initialize input -3982 # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment") -3983 # . . push args -3984 68/push "f3/m1 0f/m2 ab/m3 # comment"/imm32 -3985 68/push _test-input-stream/imm32 -3986 # . . call -3987 e8/call write/disp32 -3988 # . . discard args -3989 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3990 # convert-instruction(_test-input-stream, _test-output-buffered-file) -3991 # . . push args -3992 68/push _test-output-buffered-file/imm32 -3993 68/push _test-input-stream/imm32 -3994 # . . call -3995 e8/call convert-instruction/disp32 -3996 # . . discard args -3997 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -3998 # check output -3999 # . flush(_test-output-buffered-file) -4000 # . . push args -4001 68/push _test-output-buffered-file/imm32 -4002 # . . call -4003 e8/call flush/disp32 -4004 # . . discard args -4005 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4006 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4032 # . check-stream-equal(_test-output-stream, "f3 0f ab # f3/m1 0f/m2 ab/m3 # comment", msg) -4033 # . . push args -4034 68/push "F - test-convert-instruction-handles-f3-0f-opcode"/imm32 -4035 68/push "f3 0f ab # f3/m1 0f/m2 ab/m3 # comment"/imm32 -4036 68/push _test-output-stream/imm32 -4037 # . . call -4038 e8/call check-stream-equal/disp32 -4039 # . . discard args -4040 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4041 # . epilog -4042 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4043 5d/pop-to-EBP -4044 c3/return -4045 -4046 test-convert-instruction-handles-unused-opcodes: -4047 # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes -4048 # . prolog -4049 55/push-EBP -4050 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4051 # setup -4052 # . clear-stream(_test-input-stream) -4053 # . . push args -4054 68/push _test-input-stream/imm32 -4055 # . . call -4056 e8/call clear-stream/disp32 -4057 # . . discard args -4058 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4059 # . clear-stream(_test-output-stream) -4060 # . . push args -4061 68/push _test-output-stream/imm32 -4062 # . . call -4063 e8/call clear-stream/disp32 -4064 # . . discard args -4065 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4066 # . clear-stream(_test-output-buffered-file+4) -4067 # . . push args -4068 b8/copy-to-EAX _test-output-buffered-file/imm32 -4069 05/add-to-EAX 4/imm32 -4070 50/push-EAX -4071 # . . call -4072 e8/call clear-stream/disp32 -4073 # . . discard args -4074 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4075 # initialize input -4076 # . write(_test-input-stream, "ab/m1 cd/m2 # comment") -4077 # . . push args -4078 68/push "ab/m1 cd/m2 # comment"/imm32 -4079 68/push _test-input-stream/imm32 -4080 # . . call -4081 e8/call write/disp32 -4082 # . . discard args -4083 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4084 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4085 # . . push args -4086 68/push _test-output-buffered-file/imm32 -4087 68/push _test-input-stream/imm32 -4088 # . . call -4089 e8/call convert-instruction/disp32 -4090 # . . discard args -4091 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4092 # check output -4093 # . flush(_test-output-buffered-file) -4094 # . . push args -4095 68/push _test-output-buffered-file/imm32 -4096 # . . call -4097 e8/call flush/disp32 -4098 # . . discard args -4099 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4100 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4126 # . check-stream-equal(_test-output-stream, "ab # f3/m1 0f/m2 ab/m3 # comment", msg) -4127 # . . push args -4128 68/push "F - test-convert-instruction-handles-unused-opcodes"/imm32 -4129 68/push "ab # ab/m1 cd/m2 # comment"/imm32 -4130 68/push _test-output-stream/imm32 -4131 # . . call -4132 e8/call check-stream-equal/disp32 -4133 # . . discard args -4134 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4135 # . epilog -4136 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4137 5d/pop-to-EBP -4138 c3/return -4139 -4140 test-convert-instruction-handles-unused-second-opcodes: -4141 # if the second opcode isn't 0f, don't include further opcodes -4142 # . prolog -4143 55/push-EBP -4144 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4145 # setup -4146 # . clear-stream(_test-input-stream) -4147 # . . push args -4148 68/push _test-input-stream/imm32 -4149 # . . call -4150 e8/call clear-stream/disp32 -4151 # . . discard args -4152 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4153 # . clear-stream(_test-output-stream) -4154 # . . push args -4155 68/push _test-output-stream/imm32 -4156 # . . call -4157 e8/call clear-stream/disp32 -4158 # . . discard args -4159 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4160 # . clear-stream(_test-output-buffered-file+4) -4161 # . . push args -4162 b8/copy-to-EAX _test-output-buffered-file/imm32 -4163 05/add-to-EAX 4/imm32 -4164 50/push-EAX -4165 # . . call -4166 e8/call clear-stream/disp32 -4167 # . . discard args -4168 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4169 # initialize input -4170 # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment") -4171 # . . push args -4172 68/push "f2/m1 ab/m2 cd/m3 # comment"/imm32 -4173 68/push _test-input-stream/imm32 -4174 # . . call -4175 e8/call write/disp32 -4176 # . . discard args -4177 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4178 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4179 # . . push args -4180 68/push _test-output-buffered-file/imm32 -4181 68/push _test-input-stream/imm32 -4182 # . . call -4183 e8/call convert-instruction/disp32 -4184 # . . discard args -4185 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4186 # check output -4187 # . flush(_test-output-buffered-file) -4188 # . . push args -4189 68/push _test-output-buffered-file/imm32 -4190 # . . call -4191 e8/call flush/disp32 -4192 # . . discard args -4193 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4194 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4220 # . check-stream-equal(_test-output-stream, "f2 ab # f2/m1 ab/m2 cd/m3 # comment", msg) -4221 # . . push args -4222 68/push "F - test-convert-instruction-handles-unused-second-opcodes"/imm32 -4223 68/push "f2 ab # f2/m1 ab/m2 cd/m3 # comment"/imm32 -4224 68/push _test-output-stream/imm32 -4225 # . . call -4226 e8/call check-stream-equal/disp32 -4227 # . . discard args -4228 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4229 # . epilog -4230 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4231 5d/pop-to-EBP -4232 c3/return -4233 -4234 test-convert-instruction-handles-unused-second-opcodes-2: -4235 # if the second opcode isn't 0f, don't include further opcodes -4236 # . prolog -4237 55/push-EBP -4238 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4239 # setup -4240 # . clear-stream(_test-input-stream) -4241 # . . push args -4242 68/push _test-input-stream/imm32 -4243 # . . call -4244 e8/call clear-stream/disp32 -4245 # . . discard args -4246 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4247 # . clear-stream(_test-output-stream) -4248 # . . push args -4249 68/push _test-output-stream/imm32 -4250 # . . call -4251 e8/call clear-stream/disp32 -4252 # . . discard args -4253 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4254 # . clear-stream(_test-output-buffered-file+4) -4255 # . . push args -4256 b8/copy-to-EAX _test-output-buffered-file/imm32 -4257 05/add-to-EAX 4/imm32 -4258 50/push-EAX -4259 # . . call -4260 e8/call clear-stream/disp32 -4261 # . . discard args -4262 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4263 # initialize input -4264 # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment") -4265 # . . push args -4266 68/push "f3/m1 ab/m2 cd/m3 # comment"/imm32 -4267 68/push _test-input-stream/imm32 -4268 # . . call -4269 e8/call write/disp32 -4270 # . . discard args -4271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4272 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4273 # . . push args -4274 68/push _test-output-buffered-file/imm32 -4275 68/push _test-input-stream/imm32 -4276 # . . call -4277 e8/call convert-instruction/disp32 -4278 # . . discard args -4279 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4280 # check output -4281 # . flush(_test-output-buffered-file) -4282 # . . push args -4283 68/push _test-output-buffered-file/imm32 -4284 # . . call -4285 e8/call flush/disp32 -4286 # . . discard args -4287 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4288 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4314 # . check-stream-equal(_test-output-stream, "f3 ab # f3/m1 ab/m2 cd/m3 # comment", msg) -4315 # . . push args -4316 68/push "F - test-convert-instruction-handles-unused-second-opcodes"/imm32 -4317 68/push "f3 ab # f3/m1 ab/m2 cd/m3 # comment"/imm32 -4318 68/push _test-output-stream/imm32 -4319 # . . call -4320 e8/call check-stream-equal/disp32 -4321 # . . discard args -4322 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4323 # . epilog -4324 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4325 5d/pop-to-EBP -4326 c3/return -4327 -4328 test-convert-instruction-emits-modrm-byte: -4329 # pack mod, rm32 and r32 operands into ModR/M byte -4330 # . prolog -4331 55/push-EBP -4332 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4333 # setup -4334 # . clear-stream(_test-input-stream) -4335 # . . push args -4336 68/push _test-input-stream/imm32 -4337 # . . call -4338 e8/call clear-stream/disp32 -4339 # . . discard args -4340 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4341 # . clear-stream(_test-output-stream) -4342 # . . push args -4343 68/push _test-output-stream/imm32 -4344 # . . call -4345 e8/call clear-stream/disp32 -4346 # . . discard args -4347 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4348 # . clear-stream(_test-output-buffered-file+4) -4349 # . . push args -4350 b8/copy-to-EAX _test-output-buffered-file/imm32 -4351 05/add-to-EAX 4/imm32 -4352 50/push-EAX -4353 # . . call -4354 e8/call clear-stream/disp32 -4355 # . . discard args -4356 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4357 # initialize input -4358 # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32") -4359 # . . push args -4360 68/push "8b/copy 0/mod 0/rm32 1/r32"/imm32 -4361 68/push _test-input-stream/imm32 -4362 # . . call -4363 e8/call write/disp32 -4364 # . . discard args -4365 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4366 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4367 # . . push args -4368 68/push _test-output-buffered-file/imm32 -4369 68/push _test-input-stream/imm32 -4370 # . . call -4371 e8/call convert-instruction/disp32 -4372 # . . discard args -4373 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4374 # check output -4375 # . flush(_test-output-buffered-file) -4376 # . . push args -4377 68/push _test-output-buffered-file/imm32 -4378 # . . call -4379 e8/call flush/disp32 -4380 # . . discard args -4381 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4382 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4408 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 0/rm32 1/r32", msg) -4409 # . . push args -4410 68/push "F - test-convert-instruction-emits-modrm-byte"/imm32 -4411 68/push "8b 08 # 8b/copy 0/mod 0/rm32 1/r32"/imm32 -4412 68/push _test-output-stream/imm32 -4413 # . . call -4414 e8/call check-stream-equal/disp32 -4415 # . . discard args -4416 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4417 # . epilog -4418 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4419 5d/pop-to-EBP -4420 c3/return -4421 -4422 test-convert-instruction-emits-modrm-byte-from-subop: -4423 # pack mod, rm32 and subop operands into ModR/M byte -4424 # . prolog -4425 55/push-EBP -4426 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4427 # setup -4428 # . clear-stream(_test-input-stream) -4429 # . . push args -4430 68/push _test-input-stream/imm32 -4431 # . . call -4432 e8/call clear-stream/disp32 -4433 # . . discard args -4434 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4435 # . clear-stream(_test-output-stream) -4436 # . . push args -4437 68/push _test-output-stream/imm32 -4438 # . . call -4439 e8/call clear-stream/disp32 -4440 # . . discard args -4441 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4442 # . clear-stream(_test-output-buffered-file+4) -4443 # . . push args -4444 b8/copy-to-EAX _test-output-buffered-file/imm32 -4445 05/add-to-EAX 4/imm32 -4446 50/push-EAX -4447 # . . call -4448 e8/call clear-stream/disp32 -4449 # . . discard args -4450 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4451 # initialize input -4452 # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32") -4453 # . . push args -4454 68/push "ff 6/subop/push 0/mod 0/rm32"/imm32 -4455 68/push _test-input-stream/imm32 -4456 # . . call -4457 e8/call write/disp32 -4458 # . . discard args -4459 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4460 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4461 # . . push args -4462 68/push _test-output-buffered-file/imm32 -4463 68/push _test-input-stream/imm32 -4464 # . . call -4465 e8/call convert-instruction/disp32 -4466 # . . discard args -4467 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4468 # check output -4469 # . flush(_test-output-buffered-file) -4470 # . . push args -4471 68/push _test-output-buffered-file/imm32 -4472 # . . call -4473 e8/call flush/disp32 -4474 # . . discard args -4475 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4476 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4502 # . check-stream-equal(_test-output-stream, "ff 30 # ff 6/subop/push 0/mod 0/rm32", msg) -4503 # . . push args -4504 68/push "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32 -4505 68/push "ff 30 # ff 6/subop/push 0/mod 0/rm32"/imm32 -4506 68/push _test-output-stream/imm32 -4507 # . . call -4508 e8/call check-stream-equal/disp32 -4509 # . . discard args -4510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4511 # . epilog -4512 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4513 5d/pop-to-EBP -4514 c3/return -4515 -4516 test-convert-instruction-emits-modrm-byte-with-missing-mod: -4517 # pack rm32 and r32 operands into ModR/M byte -4518 # . prolog -4519 55/push-EBP -4520 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4521 # setup -4522 # . clear-stream(_test-input-stream) -4523 # . . push args -4524 68/push _test-input-stream/imm32 -4525 # . . call -4526 e8/call clear-stream/disp32 -4527 # . . discard args -4528 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4529 # . clear-stream(_test-output-stream) -4530 # . . push args -4531 68/push _test-output-stream/imm32 -4532 # . . call -4533 e8/call clear-stream/disp32 -4534 # . . discard args -4535 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4536 # . clear-stream(_test-output-buffered-file+4) -4537 # . . push args -4538 b8/copy-to-EAX _test-output-buffered-file/imm32 -4539 05/add-to-EAX 4/imm32 -4540 50/push-EAX -4541 # . . call -4542 e8/call clear-stream/disp32 -4543 # . . discard args -4544 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4545 # initialize input -4546 # . write(_test-input-stream, "8b/copy 0/rm32 1/r32") -4547 # . . push args -4548 68/push "8b/copy 0/rm32 1/r32"/imm32 -4549 68/push _test-input-stream/imm32 -4550 # . . call -4551 e8/call write/disp32 -4552 # . . discard args -4553 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4554 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4555 # . . push args -4556 68/push _test-output-buffered-file/imm32 -4557 68/push _test-input-stream/imm32 -4558 # . . call -4559 e8/call convert-instruction/disp32 -4560 # . . discard args -4561 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4562 # check output -4563 # . flush(_test-output-buffered-file) -4564 # . . push args -4565 68/push _test-output-buffered-file/imm32 -4566 # . . call -4567 e8/call flush/disp32 -4568 # . . discard args -4569 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4570 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4596 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/rm32 1/r32", msg) -4597 # . . push args -4598 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32 -4599 68/push "8b 08 # 8b/copy 0/rm32 1/r32"/imm32 -4600 68/push _test-output-stream/imm32 -4601 # . . call -4602 e8/call check-stream-equal/disp32 -4603 # . . discard args -4604 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4605 # . epilog -4606 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4607 5d/pop-to-EBP -4608 c3/return -4609 -4610 test-convert-instruction-emits-modrm-byte-with-missing-rm32: -4611 # pack mod and r32 operands into ModR/M byte -4612 # . prolog -4613 55/push-EBP -4614 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4615 # setup -4616 # . clear-stream(_test-input-stream) -4617 # . . push args -4618 68/push _test-input-stream/imm32 -4619 # . . call -4620 e8/call clear-stream/disp32 -4621 # . . discard args -4622 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4623 # . clear-stream(_test-output-stream) -4624 # . . push args -4625 68/push _test-output-stream/imm32 -4626 # . . call -4627 e8/call clear-stream/disp32 -4628 # . . discard args -4629 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4630 # . clear-stream(_test-output-buffered-file+4) -4631 # . . push args -4632 b8/copy-to-EAX _test-output-buffered-file/imm32 -4633 05/add-to-EAX 4/imm32 -4634 50/push-EAX -4635 # . . call -4636 e8/call clear-stream/disp32 -4637 # . . discard args -4638 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4639 # initialize input -4640 # . write(_test-input-stream, "8b/copy 0/mod 1/r32") -4641 # . . push args -4642 68/push "8b/copy 0/mod 1/r32"/imm32 -4643 68/push _test-input-stream/imm32 -4644 # . . call -4645 e8/call write/disp32 -4646 # . . discard args -4647 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4648 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4649 # . . push args -4650 68/push _test-output-buffered-file/imm32 -4651 68/push _test-input-stream/imm32 -4652 # . . call -4653 e8/call convert-instruction/disp32 -4654 # . . discard args -4655 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4656 # check output -4657 # . flush(_test-output-buffered-file) -4658 # . . push args -4659 68/push _test-output-buffered-file/imm32 -4660 # . . call -4661 e8/call flush/disp32 -4662 # . . discard args -4663 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4664 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4690 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 1/r32", msg) -4691 # . . push args -4692 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32 -4693 68/push "8b 08 # 8b/copy 0/mod 1/r32"/imm32 -4694 68/push _test-output-stream/imm32 -4695 # . . call -4696 e8/call check-stream-equal/disp32 -4697 # . . discard args -4698 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4699 # . epilog -4700 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4701 5d/pop-to-EBP -4702 c3/return -4703 -4704 test-convert-instruction-emits-modrm-byte-with-missing-r32: -4705 # pack mod and rm32 operands into ModR/M byte -4706 # . prolog -4707 55/push-EBP -4708 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4709 # setup -4710 # . clear-stream(_test-input-stream) -4711 # . . push args -4712 68/push _test-input-stream/imm32 -4713 # . . call -4714 e8/call clear-stream/disp32 -4715 # . . discard args -4716 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4717 # . clear-stream(_test-output-stream) -4718 # . . push args -4719 68/push _test-output-stream/imm32 -4720 # . . call -4721 e8/call clear-stream/disp32 -4722 # . . discard args -4723 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4724 # . clear-stream(_test-output-buffered-file+4) -4725 # . . push args -4726 b8/copy-to-EAX _test-output-buffered-file/imm32 -4727 05/add-to-EAX 4/imm32 -4728 50/push-EAX -4729 # . . call -4730 e8/call clear-stream/disp32 -4731 # . . discard args -4732 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4733 # initialize input -4734 # . write(_test-input-stream, "8b/copy 0/mod 0/rm32") -4735 # . . push args -4736 68/push "8b/copy 0/mod 0/rm32"/imm32 -4737 68/push _test-input-stream/imm32 -4738 # . . call -4739 e8/call write/disp32 -4740 # . . discard args -4741 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4742 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4743 # . . push args -4744 68/push _test-output-buffered-file/imm32 -4745 68/push _test-input-stream/imm32 -4746 # . . call -4747 e8/call convert-instruction/disp32 -4748 # . . discard args -4749 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4750 # check output -4751 # . flush(_test-output-buffered-file) -4752 # . . push args -4753 68/push _test-output-buffered-file/imm32 -4754 # . . call -4755 e8/call flush/disp32 -4756 # . . discard args -4757 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4758 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4784 # . check-stream-equal(_test-output-stream, "8b 00 # 8b/copy 0/mod 0/rm32", msg) -4785 # . . push args -4786 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32 -4787 68/push "8b 00 # 8b/copy 0/mod 0/rm32"/imm32 -4788 68/push _test-output-stream/imm32 -4789 # . . call -4790 e8/call check-stream-equal/disp32 -4791 # . . discard args -4792 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4793 # . epilog -4794 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4795 5d/pop-to-EBP -4796 c3/return -4797 -4798 test-convert-instruction-emits-sib-byte: -4799 # pack base, index and scale operands into SIB byte -4800 # . prolog -4801 55/push-EBP -4802 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4803 # setup -4804 # . clear-stream(_test-input-stream) -4805 # . . push args -4806 68/push _test-input-stream/imm32 -4807 # . . call -4808 e8/call clear-stream/disp32 -4809 # . . discard args -4810 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4811 # . clear-stream(_test-output-stream) -4812 # . . push args -4813 68/push _test-output-stream/imm32 -4814 # . . call -4815 e8/call clear-stream/disp32 -4816 # . . discard args -4817 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4818 # . clear-stream(_test-output-buffered-file+4) -4819 # . . push args -4820 b8/copy-to-EAX _test-output-buffered-file/imm32 -4821 05/add-to-EAX 4/imm32 -4822 50/push-EAX -4823 # . . call -4824 e8/call clear-stream/disp32 -4825 # . . discard args -4826 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4827 # initialize input -4828 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale") -4829 # . . push args -4830 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32 -4831 68/push _test-input-stream/imm32 -4832 # . . call -4833 e8/call write/disp32 -4834 # . . discard args -4835 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4836 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4837 # . . push args -4838 68/push _test-output-buffered-file/imm32 -4839 68/push _test-input-stream/imm32 -4840 # . . call -4841 e8/call convert-instruction/disp32 -4842 # . . discard args -4843 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4844 # check output -4845 # . flush(_test-output-buffered-file) -4846 # . . push args -4847 68/push _test-output-buffered-file/imm32 -4848 # . . call -4849 e8/call flush/disp32 -4850 # . . discard args -4851 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4852 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4878 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg) -4879 # . . push args -4880 68/push "F - test-convert-instruction-emits-sib-byte"/imm32 -4881 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32 -4882 68/push _test-output-stream/imm32 -4883 # . . call -4884 e8/call check-stream-equal/disp32 -4885 # . . discard args -4886 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4887 # . epilog -4888 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4889 5d/pop-to-EBP -4890 c3/return -4891 -4892 test-convert-instruction-emits-sib-byte-with-missing-base: -4893 # pack index and scale operands into SIB byte -4894 # . prolog -4895 55/push-EBP -4896 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4897 # setup -4898 # . clear-stream(_test-input-stream) -4899 # . . push args -4900 68/push _test-input-stream/imm32 -4901 # . . call -4902 e8/call clear-stream/disp32 -4903 # . . discard args -4904 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4905 # . clear-stream(_test-output-stream) -4906 # . . push args -4907 68/push _test-output-stream/imm32 -4908 # . . call -4909 e8/call clear-stream/disp32 -4910 # . . discard args -4911 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4912 # . clear-stream(_test-output-buffered-file+4) -4913 # . . push args -4914 b8/copy-to-EAX _test-output-buffered-file/imm32 -4915 05/add-to-EAX 4/imm32 -4916 50/push-EAX -4917 # . . call -4918 e8/call clear-stream/disp32 -4919 # . . discard args -4920 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4921 # initialize input -4922 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale") -4923 # . . push args -4924 68/push "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32 -4925 68/push _test-input-stream/imm32 -4926 # . . call -4927 e8/call write/disp32 -4928 # . . discard args -4929 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4930 # convert-instruction(_test-input-stream, _test-output-buffered-file) -4931 # . . push args -4932 68/push _test-output-buffered-file/imm32 -4933 68/push _test-input-stream/imm32 -4934 # . . call -4935 e8/call convert-instruction/disp32 -4936 # . . discard args -4937 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -4938 # check output -4939 # . flush(_test-output-buffered-file) -4940 # . . push args -4941 68/push _test-output-buffered-file/imm32 -4942 # . . call -4943 e8/call flush/disp32 -4944 # . . discard args -4945 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4946 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -4972 # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg) -4973 # . . push args -4974 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32 -4975 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32 -4976 68/push _test-output-stream/imm32 -4977 # . . call -4978 e8/call check-stream-equal/disp32 -4979 # . . discard args -4980 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -4981 # . epilog -4982 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -4983 5d/pop-to-EBP -4984 c3/return -4985 -4986 test-convert-instruction-emits-sib-byte-with-missing-index: -4987 # pack base and scale operands into SIB byte -4988 # . prolog -4989 55/push-EBP -4990 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -4991 # setup -4992 # . clear-stream(_test-input-stream) -4993 # . . push args -4994 68/push _test-input-stream/imm32 -4995 # . . call -4996 e8/call clear-stream/disp32 -4997 # . . discard args -4998 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -4999 # . clear-stream(_test-output-stream) -5000 # . . push args -5001 68/push _test-output-stream/imm32 -5002 # . . call -5003 e8/call clear-stream/disp32 -5004 # . . discard args -5005 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5006 # . clear-stream(_test-output-buffered-file+4) -5007 # . . push args -5008 b8/copy-to-EAX _test-output-buffered-file/imm32 -5009 05/add-to-EAX 4/imm32 -5010 50/push-EAX -5011 # . . call -5012 e8/call clear-stream/disp32 -5013 # . . discard args -5014 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5015 # initialize input -5016 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale") -5017 # . . push args -5018 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32 -5019 68/push _test-input-stream/imm32 -5020 # . . call -5021 e8/call write/disp32 -5022 # . . discard args -5023 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5024 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5025 # . . push args -5026 68/push _test-output-buffered-file/imm32 -5027 68/push _test-input-stream/imm32 -5028 # . . call -5029 e8/call convert-instruction/disp32 -5030 # . . discard args -5031 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5032 # check output -5033 # . flush(_test-output-buffered-file) -5034 # . . push args -5035 68/push _test-output-buffered-file/imm32 -5036 # . . call -5037 e8/call flush/disp32 -5038 # . . discard args -5039 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5040 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5066 # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg) -5067 # . . push args -5068 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32 -5069 68/push "8b 0c 00 # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32 -5070 68/push _test-output-stream/imm32 -5071 # . . call -5072 e8/call check-stream-equal/disp32 -5073 # . . discard args -5074 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5075 # . epilog -5076 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5077 5d/pop-to-EBP -5078 c3/return -5079 -5080 test-convert-instruction-emits-sib-byte-with-missing-scale: -5081 # pack base and index operands into SIB byte -5082 # . prolog -5083 55/push-EBP -5084 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5085 # setup -5086 # . clear-stream(_test-input-stream) -5087 # . . push args -5088 68/push _test-input-stream/imm32 -5089 # . . call -5090 e8/call clear-stream/disp32 -5091 # . . discard args -5092 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5093 # . clear-stream(_test-output-stream) -5094 # . . push args -5095 68/push _test-output-stream/imm32 -5096 # . . call -5097 e8/call clear-stream/disp32 -5098 # . . discard args -5099 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5100 # . clear-stream(_test-output-buffered-file+4) -5101 # . . push args -5102 b8/copy-to-EAX _test-output-buffered-file/imm32 -5103 05/add-to-EAX 4/imm32 -5104 50/push-EAX -5105 # . . call -5106 e8/call clear-stream/disp32 -5107 # . . discard args -5108 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5109 # initialize input -5110 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index") -5111 # . . push args -5112 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32 -5113 68/push _test-input-stream/imm32 -5114 # . . call -5115 e8/call write/disp32 -5116 # . . discard args -5117 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5118 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5119 # . . push args -5120 68/push _test-output-buffered-file/imm32 -5121 68/push _test-input-stream/imm32 -5122 # . . call -5123 e8/call convert-instruction/disp32 -5124 # . . discard args -5125 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5126 # check output -5127 # . flush(_test-output-buffered-file) -5128 # . . push args -5129 68/push _test-output-buffered-file/imm32 -5130 # . . call -5131 e8/call flush/disp32 -5132 # . . discard args -5133 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5134 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5160 # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg) -5161 # . . push args -5162 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32 -5163 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32 -5164 68/push _test-output-stream/imm32 -5165 # . . call -5166 e8/call check-stream-equal/disp32 -5167 # . . discard args -5168 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5169 # . epilog -5170 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5171 5d/pop-to-EBP -5172 c3/return -5173 -5174 test-convert-instruction-handles-disp32-operand: -5175 # expand /disp32 operand into 4 bytes -5176 # . prolog -5177 55/push-EBP -5178 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5179 # setup -5180 # . clear-stream(_test-input-stream) -5181 # . . push args -5182 68/push _test-input-stream/imm32 -5183 # . . call -5184 e8/call clear-stream/disp32 -5185 # . . discard args -5186 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5187 # . clear-stream(_test-output-stream) -5188 # . . push args -5189 68/push _test-output-stream/imm32 -5190 # . . call -5191 e8/call clear-stream/disp32 -5192 # . . discard args -5193 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5194 # . clear-stream(_test-output-buffered-file+4) -5195 # . . push args -5196 b8/copy-to-EAX _test-output-buffered-file/imm32 -5197 05/add-to-EAX 4/imm32 -5198 50/push-EAX -5199 # . . call -5200 e8/call clear-stream/disp32 -5201 # . . discard args -5202 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5203 # initialize input -5204 # . write(_test-input-stream, "e8/call 20/disp32") -5205 # . . push args -5206 68/push "e8/call 20/disp32"/imm32 -5207 68/push _test-input-stream/imm32 -5208 # . . call -5209 e8/call write/disp32 -5210 # . . discard args -5211 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5212 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5213 # . . push args -5214 68/push _test-output-buffered-file/imm32 -5215 68/push _test-input-stream/imm32 -5216 # . . call -5217 e8/call convert-instruction/disp32 -5218 # . . discard args -5219 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5220 # check output -5221 # . flush(_test-output-buffered-file) -5222 # . . push args -5223 68/push _test-output-buffered-file/imm32 -5224 # . . call -5225 e8/call flush/disp32 -5226 # . . discard args -5227 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5228 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5254 # . check-stream-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg) -5255 # . . push args -5256 68/push "F - test-convert-instruction-handles-disp32-operand"/imm32 -5257 68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32 -5258 68/push _test-output-stream/imm32 -5259 # . . call -5260 e8/call check-stream-equal/disp32 -5261 # . . discard args -5262 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5263 # . epilog -5264 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5265 5d/pop-to-EBP -5266 c3/return -5267 -5268 test-convert-instruction-handles-disp16-operand: -5269 # expand /disp16 operand into 2 bytes -5270 # . prolog -5271 55/push-EBP -5272 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5273 # setup -5274 # . clear-stream(_test-input-stream) -5275 # . . push args -5276 68/push _test-input-stream/imm32 -5277 # . . call -5278 e8/call clear-stream/disp32 -5279 # . . discard args -5280 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5281 # . clear-stream(_test-output-stream) -5282 # . . push args -5283 68/push _test-output-stream/imm32 -5284 # . . call -5285 e8/call clear-stream/disp32 -5286 # . . discard args -5287 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5288 # . clear-stream(_test-output-buffered-file+4) -5289 # . . push args -5290 b8/copy-to-EAX _test-output-buffered-file/imm32 -5291 05/add-to-EAX 4/imm32 -5292 50/push-EAX -5293 # . . call -5294 e8/call clear-stream/disp32 -5295 # . . discard args -5296 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5297 # initialize input -5298 # . write(_test-input-stream, "e8/call 20/disp16") -5299 # . . push args -5300 68/push "e8/call 20/disp16"/imm32 # not a valid instruction -5301 68/push _test-input-stream/imm32 -5302 # . . call -5303 e8/call write/disp32 -5304 # . . discard args -5305 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5306 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5307 # . . push args -5308 68/push _test-output-buffered-file/imm32 -5309 68/push _test-input-stream/imm32 -5310 # . . call -5311 e8/call convert-instruction/disp32 -5312 # . . discard args -5313 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5314 # check output -5315 # . flush(_test-output-buffered-file) -5316 # . . push args -5317 68/push _test-output-buffered-file/imm32 -5318 # . . call -5319 e8/call flush/disp32 -5320 # . . discard args -5321 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5322 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5348 # . check-stream-equal(_test-output-stream, "e8 20 00 # e8/call 20/disp16", msg) -5349 # . . push args -5350 68/push "F - test-convert-instruction-handles-disp16-operand"/imm32 -5351 68/push "e8 20 00 # e8/call 20/disp16"/imm32 -5352 68/push _test-output-stream/imm32 -5353 # . . call -5354 e8/call check-stream-equal/disp32 -5355 # . . discard args -5356 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5357 # . epilog -5358 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5359 5d/pop-to-EBP -5360 c3/return -5361 -5362 test-convert-instruction-handles-disp8-operand: -5363 # expand /disp8 operand into 1 byte -5364 # . prolog -5365 55/push-EBP -5366 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5367 # setup -5368 # . clear-stream(_test-input-stream) -5369 # . . push args -5370 68/push _test-input-stream/imm32 -5371 # . . call -5372 e8/call clear-stream/disp32 -5373 # . . discard args -5374 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5375 # . clear-stream(_test-output-stream) -5376 # . . push args -5377 68/push _test-output-stream/imm32 -5378 # . . call -5379 e8/call clear-stream/disp32 -5380 # . . discard args -5381 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5382 # . clear-stream(_test-output-buffered-file+4) -5383 # . . push args -5384 b8/copy-to-EAX _test-output-buffered-file/imm32 -5385 05/add-to-EAX 4/imm32 -5386 50/push-EAX -5387 # . . call -5388 e8/call clear-stream/disp32 -5389 # . . discard args -5390 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5391 # initialize input -5392 # . write(_test-input-stream, "eb/jump 20/disp8") -5393 # . . push args -5394 68/push "eb/jump 20/disp8"/imm32 -5395 68/push _test-input-stream/imm32 -5396 # . . call -5397 e8/call write/disp32 -5398 # . . discard args -5399 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5400 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5401 # . . push args -5402 68/push _test-output-buffered-file/imm32 -5403 68/push _test-input-stream/imm32 -5404 # . . call -5405 e8/call convert-instruction/disp32 -5406 # . . discard args -5407 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5408 # check output -5409 # . flush(_test-output-buffered-file) -5410 # . . push args -5411 68/push _test-output-buffered-file/imm32 -5412 # . . call -5413 e8/call flush/disp32 -5414 # . . discard args -5415 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5416 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5442 # . check-stream-equal(_test-output-stream, "eb 20 # eb/jump 20/disp8", msg) -5443 # . . push args -5444 68/push "F - test-convert-instruction-handles-disp8-operand"/imm32 -5445 68/push "eb 20 # eb/jump 20/disp8"/imm32 -5446 68/push _test-output-stream/imm32 -5447 # . . call -5448 e8/call check-stream-equal/disp32 -5449 # . . discard args -5450 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5451 # . epilog -5452 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5453 5d/pop-to-EBP -5454 c3/return -5455 -5456 test-convert-instruction-handles-disp8-name: -5457 # pass /disp8 name directly through -5458 # . prolog -5459 55/push-EBP -5460 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5461 # setup -5462 # . clear-stream(_test-input-stream) -5463 # . . push args -5464 68/push _test-input-stream/imm32 -5465 # . . call -5466 e8/call clear-stream/disp32 -5467 # . . discard args -5468 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5469 # . clear-stream(_test-output-stream) -5470 # . . push args -5471 68/push _test-output-stream/imm32 -5472 # . . call -5473 e8/call clear-stream/disp32 -5474 # . . discard args -5475 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5476 # . clear-stream(_test-output-buffered-file+4) -5477 # . . push args -5478 b8/copy-to-EAX _test-output-buffered-file/imm32 -5479 05/add-to-EAX 4/imm32 -5480 50/push-EAX -5481 # . . call -5482 e8/call clear-stream/disp32 -5483 # . . discard args -5484 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5485 # initialize input -5486 # . write(_test-input-stream, "eb/jump xyz/disp8") -5487 # . . push args -5488 68/push "eb/jump xyz/disp8"/imm32 -5489 68/push _test-input-stream/imm32 -5490 # . . call -5491 e8/call write/disp32 -5492 # . . discard args -5493 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5494 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5495 # . . push args -5496 68/push _test-output-buffered-file/imm32 -5497 68/push _test-input-stream/imm32 -5498 # . . call -5499 e8/call convert-instruction/disp32 -5500 # . . discard args -5501 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5502 # check output -5503 # . flush(_test-output-buffered-file) -5504 # . . push args -5505 68/push _test-output-buffered-file/imm32 -5506 # . . call -5507 e8/call flush/disp32 -5508 # . . discard args -5509 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5510 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5536 # . check-stream-equal(_test-output-stream, "eb xyz/disp8 # eb/jump xyz/disp8", msg) -5537 # . . push args -5538 68/push "F - test-convert-instruction-handles-disp8-name"/imm32 -5539 68/push "eb xyz/disp8 # eb/jump xyz/disp8"/imm32 -5540 68/push _test-output-stream/imm32 -5541 # . . call -5542 e8/call check-stream-equal/disp32 -5543 # . . discard args -5544 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5545 # . epilog -5546 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5547 5d/pop-to-EBP -5548 c3/return -5549 -5550 test-convert-instruction-handles-imm32-operand: -5551 # expand /imm32 operand into 4 bytes -5552 # . prolog -5553 55/push-EBP -5554 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5555 # setup -5556 # . clear-stream(_test-input-stream) -5557 # . . push args -5558 68/push _test-input-stream/imm32 -5559 # . . call -5560 e8/call clear-stream/disp32 -5561 # . . discard args -5562 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5563 # . clear-stream(_test-output-stream) -5564 # . . push args -5565 68/push _test-output-stream/imm32 -5566 # . . call -5567 e8/call clear-stream/disp32 -5568 # . . discard args -5569 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5570 # . clear-stream(_test-output-buffered-file+4) -5571 # . . push args -5572 b8/copy-to-EAX _test-output-buffered-file/imm32 -5573 05/add-to-EAX 4/imm32 -5574 50/push-EAX -5575 # . . call -5576 e8/call clear-stream/disp32 -5577 # . . discard args -5578 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5579 # initialize input -5580 # . write(_test-input-stream, "68/push 0x20/imm32") -5581 # . . push args -5582 68/push "68/push 0x20/imm32"/imm32 -5583 68/push _test-input-stream/imm32 -5584 # . . call -5585 e8/call write/disp32 -5586 # . . discard args -5587 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5588 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5589 # . . push args -5590 68/push _test-output-buffered-file/imm32 -5591 68/push _test-input-stream/imm32 -5592 # . . call -5593 e8/call convert-instruction/disp32 -5594 # . . discard args -5595 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5596 # check output -5597 # . flush(_test-output-buffered-file) -5598 # . . push args -5599 68/push _test-output-buffered-file/imm32 -5600 # . . call -5601 e8/call flush/disp32 -5602 # . . discard args -5603 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5604 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5630 # . check-stream-equal(_test-output-stream, "68 20 00 00 00 # 68/push 0x20/imm32", msg) -5631 # . . push args -5632 68/push "F - test-convert-instruction-handles-imm32-operand"/imm32 -5633 68/push "68 20 00 00 00 # 68/push 0x20/imm32"/imm32 -5634 68/push _test-output-stream/imm32 -5635 # . . call -5636 e8/call check-stream-equal/disp32 -5637 # . . discard args -5638 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5639 # . epilog -5640 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5641 5d/pop-to-EBP -5642 c3/return -5643 -5644 test-convert-instruction-handles-imm16-operand: -5645 # expand /imm16 operand into 2 bytes -5646 # we don't have one of these at the moment, so this expands to an invalid instruction -5647 # . prolog -5648 55/push-EBP -5649 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5650 # setup -5651 # . clear-stream(_test-input-stream) -5652 # . . push args -5653 68/push _test-input-stream/imm32 -5654 # . . call -5655 e8/call clear-stream/disp32 -5656 # . . discard args -5657 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5658 # . clear-stream(_test-output-stream) -5659 # . . push args -5660 68/push _test-output-stream/imm32 -5661 # . . call -5662 e8/call clear-stream/disp32 -5663 # . . discard args -5664 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5665 # . clear-stream(_test-output-buffered-file+4) -5666 # . . push args -5667 b8/copy-to-EAX _test-output-buffered-file/imm32 -5668 05/add-to-EAX 4/imm32 -5669 50/push-EAX -5670 # . . call -5671 e8/call clear-stream/disp32 -5672 # . . discard args -5673 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5674 # initialize input -5675 # . write(_test-input-stream, "68/push 0x20/imm16") -5676 # . . push args -5677 68/push "68/push 0x20/imm16"/imm32 # not a valid instruction -5678 68/push _test-input-stream/imm32 -5679 # . . call -5680 e8/call write/disp32 -5681 # . . discard args -5682 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5683 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5684 # . . push args -5685 68/push _test-output-buffered-file/imm32 -5686 68/push _test-input-stream/imm32 -5687 # . . call -5688 e8/call convert-instruction/disp32 -5689 # . . discard args -5690 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5691 # check output -5692 # . flush(_test-output-buffered-file) -5693 # . . push args -5694 68/push _test-output-buffered-file/imm32 -5695 # . . call -5696 e8/call flush/disp32 -5697 # . . discard args -5698 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5699 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5725 # . check-stream-equal(_test-output-stream, "68 20 00 # 68/push 0x20/imm16", msg) -5726 # . . push args -5727 68/push "F - test-convert-instruction-handles-imm16-operand"/imm32 -5728 68/push "68 20 00 # 68/push 0x20/imm16"/imm32 -5729 68/push _test-output-stream/imm32 -5730 # . . call -5731 e8/call check-stream-equal/disp32 -5732 # . . discard args -5733 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5734 # . epilog -5735 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5736 5d/pop-to-EBP -5737 c3/return -5738 -5739 test-convert-instruction-handles-imm8-operand: -5740 # expand /imm8 operand into 1 byte -5741 # we don't have one of these at the moment, so this expands to an invalid instruction -5742 # . prolog -5743 55/push-EBP -5744 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5745 # setup -5746 # . clear-stream(_test-input-stream) -5747 # . . push args -5748 68/push _test-input-stream/imm32 -5749 # . . call -5750 e8/call clear-stream/disp32 -5751 # . . discard args -5752 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5753 # . clear-stream(_test-output-stream) -5754 # . . push args -5755 68/push _test-output-stream/imm32 -5756 # . . call -5757 e8/call clear-stream/disp32 -5758 # . . discard args -5759 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5760 # . clear-stream(_test-output-buffered-file+4) -5761 # . . push args -5762 b8/copy-to-EAX _test-output-buffered-file/imm32 -5763 05/add-to-EAX 4/imm32 -5764 50/push-EAX -5765 # . . call -5766 e8/call clear-stream/disp32 -5767 # . . discard args -5768 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5769 # initialize input -5770 # . write(_test-input-stream, "68/push 0x20/imm8") -5771 # . . push args -5772 68/push "68/push 0x20/imm8"/imm32 -5773 68/push _test-input-stream/imm32 -5774 # . . call -5775 e8/call write/disp32 -5776 # . . discard args -5777 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5778 # convert-instruction(_test-input-stream, _test-output-buffered-file) -5779 # . . push args -5780 68/push _test-output-buffered-file/imm32 -5781 68/push _test-input-stream/imm32 -5782 # . . call -5783 e8/call convert-instruction/disp32 -5784 # . . discard args -5785 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5786 # check output -5787 # . flush(_test-output-buffered-file) -5788 # . . push args -5789 68/push _test-output-buffered-file/imm32 -5790 # . . call -5791 e8/call flush/disp32 -5792 # . . discard args -5793 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5794 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -5820 # . check-stream-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg) -5821 # . . push args -5822 68/push "F - test-convert-instruction-handles-imm8-operand"/imm32 -5823 68/push "68 20 # 68/push 0x20/imm8"/imm32 -5824 68/push _test-output-stream/imm32 -5825 # . . call -5826 e8/call check-stream-equal/disp32 -5827 # . . discard args -5828 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5829 # . epilog -5830 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5831 5d/pop-to-EBP -5832 c3/return -5833 -5834 # (re)compute the bounds of the next word in the line -5835 # return empty string on reaching end of file -5836 next-word: # line : (address stream byte), out : (address slice) + 108 # else if (slice-equal?(word-slice, "==")) + 109 # word-slice = next-word(line) + 110 # in-code? = slice-equal?(word-slice, "code") + 111 # write-stream-data(out, line) + 112 # else if (in-code?) + 113 # rewind-stream(line) + 114 # convert-instruction(line, out) + 115 # else + 116 # rewind-stream(line) + 117 # convert-data(line, out) + 118 # flush(out) + 119 # + 120 # . prolog + 121 55/push-EBP + 122 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 123 # . save registers + 124 50/push-EAX + 125 51/push-ECX + 126 52/push-EDX + 127 53/push-EBX + 128 # var line/ECX : (address stream byte) = stream(512) + 129 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP + 130 68/push 0x200/imm32/length + 131 68/push 0/imm32/read + 132 68/push 0/imm32/write + 133 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 134 # var word-slice/EDX = {0, 0} + 135 68/push 0/imm32/end + 136 68/push 0/imm32/start + 137 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX + 138 # var in-code?/EBX = false + 139 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX + 140 $convert:loop: + 141 # clear-stream(line) + 142 # . . push args + 143 51/push-ECX + 144 # . . call + 145 e8/call clear-stream/disp32 + 146 # . . discard args + 147 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 148 # read-line-buffered(in, line) + 149 # . . push args + 150 51/push-ECX + 151 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 152 # . . call + 153 e8/call read-line-buffered/disp32 + 154 # . . discard args + 155 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 156 $convert:check0: + 157 # if (line->write == 0) break + 158 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX + 159 0f 84/jump-if-equal $convert:break/disp32 + 160 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- + 186 # next-word(line, word-slice) + 187 # . . push args + 188 52/push-EDX + 189 51/push-ECX + 190 # . . call + 191 e8/call next-word/disp32 + 192 # . . discard args + 193 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 194 $convert:check1: + 195 # if (slice-empty?(word-slice)) write-stream-data(out, line) + 196 # . EAX = slice-empty?(word-slice) + 197 # . . push args + 198 52/push-EDX + 199 # . . call + 200 e8/call slice-empty?/disp32 + 201 # . . discard args + 202 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 203 # . if (EAX != 0) write-stream-data(out, line) + 204 3d/compare-EAX-and 0/imm32 + 205 0f 85/jump-if-not-equal $convert:pass-through/disp32 + 206 $convert:check2: + 207 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- + 249 # if (!slice-equal?(word-slice, "==")) goto next check + 250 # . EAX = slice-equal?(word-slice, "==") + 251 # . . push args + 252 68/push "=="/imm32 + 253 52/push-EDX + 254 # . . call + 255 e8/call slice-equal?/disp32 + 256 # . . discard args + 257 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 258 # . if (EAX == 0) goto check3 + 259 3d/compare-EAX-and 0/imm32 + 260 0f 84/jump-if-equal $convert:check3/disp32 + 261 # word-slice = next-word(line) + 262 # . . push args + 263 52/push-EDX + 264 51/push-ECX + 265 # . . call + 266 e8/call next-word/disp32 + 267 # . . discard args + 268 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 269 +-- 42 lines: #? # dump segment name --------------------------------------------------------------------------------------------------------------------- + 311 # in-code? = slice-equal?(word-slice, "code") + 312 # . . push args + 313 68/push "code"/imm32 + 314 52/push-EDX + 315 # . . call + 316 e8/call slice-equal?/disp32 + 317 # . . discard args + 318 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 319 # . . in-code? = EAX + 320 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + 321 # write-stream-data(out, line) + 322 eb/jump $convert:pass-through/disp8 + 323 $convert:check3: + 324 # else rewind-stream(line) + 325 # . rewind-stream(line) + 326 # . . push args + 327 51/push-ECX + 328 # . . call + 329 e8/call rewind-stream/disp32 + 330 # . . discard args + 331 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 332 # if (in-code? != 0) convert-instruction(line, out) + 333 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX + 334 74/jump-if-equal $convert:data/disp8 + 335 $convert:code: + 336 # . convert-instruction(line, out) + 337 # . . push args + 338 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 339 51/push-ECX + 340 # . . call + 341 e8/call convert-instruction/disp32 + 342 # . . discard args + 343 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 344 # . loop + 345 e9/jump $convert:loop/disp32 + 346 $convert:data: + 347 # else convert-data(line, out) + 348 # . . push args + 349 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 350 51/push-ECX + 351 # . . call + 352 e8/call convert-data/disp32 + 353 # . . discard args + 354 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 355 # . loop + 356 e9/jump $convert:loop/disp32 + 357 $convert:pass-through: + 358 # write-stream-data(out, line) + 359 # . . push args + 360 51/push-ECX + 361 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 362 # . . call + 363 e8/call write-stream-data/disp32 + 364 # . . discard args + 365 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 366 # . loop + 367 e9/jump $convert:loop/disp32 + 368 $convert:break: + 369 # flush(out) + 370 # . . push args + 371 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 372 # . . call + 373 e8/call flush/disp32 + 374 # . . discard args + 375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 376 $convert:end: + 377 # . reclaim locals + 378 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP + 379 # . restore registers + 380 5b/pop-to-EBX + 381 5a/pop-to-EDX + 382 59/pop-to-ECX + 383 58/pop-to-EAX + 384 # . epilog + 385 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 386 5d/pop-to-EBP + 387 c3/return + 388 + 389 test-convert-passes-empty-lines-through: + 390 # if a line is empty, pass it along unchanged + 391 # . prolog + 392 55/push-EBP + 393 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 394 # setup + 395 # . clear-stream(_test-input-stream) + 396 # . . push args + 397 68/push _test-input-stream/imm32 + 398 # . . call + 399 e8/call clear-stream/disp32 + 400 # . . discard args + 401 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 402 # . clear-stream(_test-input-buffered-file+4) + 403 # . . push args + 404 b8/copy-to-EAX _test-input-buffered-file/imm32 + 405 05/add-to-EAX 4/imm32 + 406 50/push-EAX + 407 # . . call + 408 e8/call clear-stream/disp32 + 409 # . . discard args + 410 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 411 # . clear-stream(_test-output-stream) + 412 # . . push args + 413 68/push _test-output-stream/imm32 + 414 # . . call + 415 e8/call clear-stream/disp32 + 416 # . . discard args + 417 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 418 # . clear-stream(_test-output-buffered-file+4) + 419 # . . push args + 420 b8/copy-to-EAX _test-output-buffered-file/imm32 + 421 05/add-to-EAX 4/imm32 + 422 50/push-EAX + 423 # . . call + 424 e8/call clear-stream/disp32 + 425 # . . discard args + 426 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 427 # write nothing to input + 428 # convert(_test-input-buffered-file, _test-output-buffered-file) + 429 # . . push args + 430 68/push _test-output-buffered-file/imm32 + 431 68/push _test-input-buffered-file/imm32 + 432 # . . call + 433 e8/call convert/disp32 + 434 # . . discard args + 435 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 436 # check that the line just passed through + 437 # . flush(_test-output-buffered-file) + 438 # . . push args + 439 68/push _test-output-buffered-file/imm32 + 440 # . . call + 441 e8/call flush/disp32 + 442 # . . discard args + 443 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 444 # . check-stream-equal(_test-output-stream, "", msg) + 445 # . . push args + 446 68/push "F - test-convert-passes-empty-lines-through"/imm32 + 447 68/push ""/imm32 + 448 68/push _test-output-stream/imm32 + 449 # . . call + 450 e8/call check-stream-equal/disp32 + 451 # . . discard args + 452 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 453 # . epilog + 454 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 455 5d/pop-to-EBP + 456 c3/return + 457 + 458 test-convert-passes-lines-with-just-whitespace-through: + 459 # if a line is empty, pass it along unchanged + 460 # . prolog + 461 55/push-EBP + 462 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 463 # setup + 464 # . clear-stream(_test-input-stream) + 465 # . . push args + 466 68/push _test-input-stream/imm32 + 467 # . . call + 468 e8/call clear-stream/disp32 + 469 # . . discard args + 470 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 471 # . clear-stream(_test-input-buffered-file+4) + 472 # . . push args + 473 b8/copy-to-EAX _test-input-buffered-file/imm32 + 474 05/add-to-EAX 4/imm32 + 475 50/push-EAX + 476 # . . call + 477 e8/call clear-stream/disp32 + 478 # . . discard args + 479 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 480 # . clear-stream(_test-output-stream) + 481 # . . push args + 482 68/push _test-output-stream/imm32 + 483 # . . call + 484 e8/call clear-stream/disp32 + 485 # . . discard args + 486 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 487 # . clear-stream(_test-output-buffered-file+4) + 488 # . . push args + 489 b8/copy-to-EAX _test-output-buffered-file/imm32 + 490 05/add-to-EAX 4/imm32 + 491 50/push-EAX + 492 # . . call + 493 e8/call clear-stream/disp32 + 494 # . . discard args + 495 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 496 # initialize input + 497 # . write(_test-input-stream, " ") + 498 # . . push args + 499 68/push " "/imm32 + 500 68/push _test-input-stream/imm32 + 501 # . . call + 502 e8/call write/disp32 + 503 # . . discard args + 504 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 505 # convert(_test-input-buffered-file, _test-output-buffered-file) + 506 # . . push args + 507 68/push _test-output-buffered-file/imm32 + 508 68/push _test-input-buffered-file/imm32 + 509 # . . call + 510 e8/call convert/disp32 + 511 # . . discard args + 512 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 513 # check that the line just passed through + 514 # . flush(_test-output-buffered-file) + 515 # . . push args + 516 68/push _test-output-buffered-file/imm32 + 517 # . . call + 518 e8/call flush/disp32 + 519 # . . discard args + 520 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 521 # . check-next-stream-line-equal(_test-output-stream, " ", msg) + 522 # . . push args + 523 68/push "F - test-convert-passes-with-just-whitespace-through"/imm32 + 524 68/push " "/imm32 + 525 68/push _test-output-stream/imm32 + 526 # . . call + 527 e8/call check-next-stream-line-equal/disp32 + 528 # . . discard args + 529 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 530 # . epilog + 531 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 532 5d/pop-to-EBP + 533 c3/return + 534 + 535 test-convert-passes-segment-headers-through: + 536 # if a line starts with '==', pass it along unchanged + 537 # . prolog + 538 55/push-EBP + 539 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 540 # setup + 541 # . clear-stream(_test-input-stream) + 542 # . . push args + 543 68/push _test-input-stream/imm32 + 544 # . . call + 545 e8/call clear-stream/disp32 + 546 # . . discard args + 547 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 548 # . clear-stream(_test-input-buffered-file+4) + 549 # . . push args + 550 b8/copy-to-EAX _test-input-buffered-file/imm32 + 551 05/add-to-EAX 4/imm32 + 552 50/push-EAX + 553 # . . call + 554 e8/call clear-stream/disp32 + 555 # . . discard args + 556 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 557 # . clear-stream(_test-output-stream) + 558 # . . push args + 559 68/push _test-output-stream/imm32 + 560 # . . call + 561 e8/call clear-stream/disp32 + 562 # . . discard args + 563 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 564 # . clear-stream(_test-output-buffered-file+4) + 565 # . . push args + 566 b8/copy-to-EAX _test-output-buffered-file/imm32 + 567 05/add-to-EAX 4/imm32 + 568 50/push-EAX + 569 # . . call + 570 e8/call clear-stream/disp32 + 571 # . . discard args + 572 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 573 # initialize input + 574 # . write(_test-input-stream, "== abcd 0x1") + 575 # . . push args + 576 68/push "== abcd 0x1"/imm32 + 577 68/push _test-input-stream/imm32 + 578 # . . call + 579 e8/call write/disp32 + 580 # . . discard args + 581 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 582 # convert(_test-input-buffered-file, _test-output-buffered-file) + 583 # . . push args + 584 68/push _test-output-buffered-file/imm32 + 585 68/push _test-input-buffered-file/imm32 + 586 # . . call + 587 e8/call convert/disp32 + 588 # . . discard args + 589 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 590 # check that the line just passed through + 591 # . flush(_test-output-buffered-file) + 592 # . . push args + 593 68/push _test-output-buffered-file/imm32 + 594 # . . call + 595 e8/call flush/disp32 + 596 # . . discard args + 597 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 598 # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg) + 599 # . . push args + 600 68/push "F - test-convert-passes-segment-headers-through"/imm32 + 601 68/push "== abcd 0x1"/imm32 + 602 68/push _test-output-stream/imm32 + 603 # . . call + 604 e8/call check-stream-equal/disp32 + 605 # . . discard args + 606 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 607 # . epilog + 608 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 609 5d/pop-to-EBP + 610 c3/return + 611 + 612 test-convert-in-data-segment: + 613 # correctly process lines in the data segment + 614 # . prolog + 615 55/push-EBP + 616 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 617 # setup + 618 # . clear-stream(_test-input-stream) + 619 # . . push args + 620 68/push _test-input-stream/imm32 + 621 # . . call + 622 e8/call clear-stream/disp32 + 623 # . . discard args + 624 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 625 # . clear-stream(_test-input-buffered-file+4) + 626 # . . push args + 627 b8/copy-to-EAX _test-input-buffered-file/imm32 + 628 05/add-to-EAX 4/imm32 + 629 50/push-EAX + 630 # . . call + 631 e8/call clear-stream/disp32 + 632 # . . discard args + 633 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 634 # . clear-stream(_test-output-stream) + 635 # . . push args + 636 68/push _test-output-stream/imm32 + 637 # . . call + 638 e8/call clear-stream/disp32 + 639 # . . discard args + 640 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 641 # . clear-stream(_test-output-buffered-file+4) + 642 # . . push args + 643 b8/copy-to-EAX _test-output-buffered-file/imm32 + 644 05/add-to-EAX 4/imm32 + 645 50/push-EAX + 646 # . . call + 647 e8/call clear-stream/disp32 + 648 # . . discard args + 649 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 650 # initialize input + 651 # == code 0x1 + 652 # == data 0x2 + 653 # 3 4/imm32 + 654 # . write(_test-input-stream, "== code 0x1") + 655 # . . push args + 656 68/push "== code 0x1\n"/imm32 + 657 68/push _test-input-stream/imm32 + 658 # . . call + 659 e8/call write/disp32 + 660 # . . discard args + 661 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 662 # . write(_test-input-stream, "== data 0x2\n") + 663 # . . push args + 664 68/push "== data 0x2\n"/imm32 + 665 68/push _test-input-stream/imm32 + 666 # . . call + 667 e8/call write/disp32 + 668 # . . discard args + 669 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 670 # . write(_test-input-stream, "3 4/imm32\n") + 671 # . . push args + 672 68/push "3 4/imm32\n"/imm32 + 673 68/push _test-input-stream/imm32 + 674 # . . call + 675 e8/call write/disp32 + 676 # . . discard args + 677 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 678 # convert(_test-input-buffered-file, _test-output-buffered-file) + 679 # . . push args + 680 68/push _test-output-buffered-file/imm32 + 681 68/push _test-input-buffered-file/imm32 + 682 # . . call + 683 e8/call convert/disp32 + 684 # . . discard args + 685 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 686 # check output + 687 +-- 26 lines: #? # debug print --------------------------------------------------------------------------------------------------------------------------- + 713 # . flush(_test-output-buffered-file) + 714 # . . push args + 715 68/push _test-output-buffered-file/imm32 + 716 # . . call + 717 e8/call flush/disp32 + 718 # . . discard args + 719 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 720 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) + 721 # . . push args + 722 68/push "F - test-convert-in-data-segment/0"/imm32 + 723 68/push "== code 0x1"/imm32 + 724 68/push _test-output-stream/imm32 + 725 # . . call + 726 e8/call check-next-stream-line-equal/disp32 + 727 # . . discard args + 728 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 729 # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) + 730 # . . push args + 731 68/push "F - test-convert-in-data-segment/1"/imm32 + 732 68/push "== data 0x2"/imm32 + 733 68/push _test-output-stream/imm32 + 734 # . . call + 735 e8/call check-next-stream-line-equal/disp32 + 736 # . . discard args + 737 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 738 # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) + 739 # . . push args + 740 68/push "F - test-convert-in-data-segment/2"/imm32 + 741 68/push "03 04 00 00 00 "/imm32 + 742 68/push _test-output-stream/imm32 + 743 # . . call + 744 e8/call check-next-stream-line-equal/disp32 + 745 # . . discard args + 746 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 747 # . epilog + 748 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 749 5d/pop-to-EBP + 750 c3/return + 751 + 752 test-convert-code-and-data-segments: + 753 # correctly process lines in both code and data segments + 754 # . prolog + 755 55/push-EBP + 756 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 757 # setup + 758 # . clear-stream(_test-input-stream) + 759 # . . push args + 760 68/push _test-input-stream/imm32 + 761 # . . call + 762 e8/call clear-stream/disp32 + 763 # . . discard args + 764 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 765 # . clear-stream(_test-input-buffered-file+4) + 766 # . . push args + 767 b8/copy-to-EAX _test-input-buffered-file/imm32 + 768 05/add-to-EAX 4/imm32 + 769 50/push-EAX + 770 # . . call + 771 e8/call clear-stream/disp32 + 772 # . . discard args + 773 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 774 # . clear-stream(_test-output-stream) + 775 # . . push args + 776 68/push _test-output-stream/imm32 + 777 # . . call + 778 e8/call clear-stream/disp32 + 779 # . . discard args + 780 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 781 # . clear-stream(_test-output-buffered-file+4) + 782 # . . push args + 783 b8/copy-to-EAX _test-output-buffered-file/imm32 + 784 05/add-to-EAX 4/imm32 + 785 50/push-EAX + 786 # . . call + 787 e8/call clear-stream/disp32 + 788 # . . discard args + 789 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 790 # initialize input + 791 # == code 0x1 + 792 # e8/call 20/disp32 + 793 # 68/push 0x20/imm8 + 794 # == data 0x2 + 795 # 3 4/imm32 + 796 # . write(_test-input-stream, "== code 0x1\n") + 797 # . . push args + 798 68/push "== code 0x1\n"/imm32 + 799 68/push _test-input-stream/imm32 + 800 # . . call + 801 e8/call write/disp32 + 802 # . . discard args + 803 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 804 # . write(_test-input-stream, "e8/call 20/disp32\n") + 805 # . . push args + 806 68/push "e8/call 20/disp32\n"/imm32 + 807 68/push _test-input-stream/imm32 + 808 # . . call + 809 e8/call write/disp32 + 810 # . . discard args + 811 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 812 # . write(_test-input-stream, "68/push 0x20/imm8\n") + 813 # . . push args + 814 68/push "68/push 0x20/imm8\n"/imm32 + 815 68/push _test-input-stream/imm32 + 816 # . . call + 817 e8/call write/disp32 + 818 # . . discard args + 819 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 820 # . write(_test-input-stream, "== data 0x2\n") + 821 # . . push args + 822 68/push "== data 0x2\n"/imm32 + 823 68/push _test-input-stream/imm32 + 824 # . . call + 825 e8/call write/disp32 + 826 # . . discard args + 827 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 828 # . write(_test-input-stream, "3 4/imm32\n") + 829 # . . push args + 830 68/push "3 4/imm32\n"/imm32 + 831 68/push _test-input-stream/imm32 + 832 # . . call + 833 e8/call write/disp32 + 834 # . . discard args + 835 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 836 # convert(_test-input-buffered-file, _test-output-buffered-file) + 837 # . . push args + 838 68/push _test-output-buffered-file/imm32 + 839 68/push _test-input-buffered-file/imm32 + 840 # . . call + 841 e8/call convert/disp32 + 842 # . . discard args + 843 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 844 # check output + 845 # == code 0x1 + 846 # e8 20 00 00 00 # e8/call 20/disp32 + 847 # 68 20 # 68/push 0x20/imm8 + 848 # == data 0x2 + 849 # 03 04 00 00 00 + 850 +-- 26 lines: #? # debug print --------------------------------------------------------------------------------------------------------------------------- + 876 # . flush(_test-output-buffered-file) + 877 # . . push args + 878 68/push _test-output-buffered-file/imm32 + 879 # . . call + 880 e8/call flush/disp32 + 881 # . . discard args + 882 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 883 # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) + 884 # . . push args + 885 68/push "F - test-convert-code-and-data-segments/0"/imm32 + 886 68/push "== code 0x1"/imm32 + 887 68/push _test-output-stream/imm32 + 888 # . . call + 889 e8/call check-next-stream-line-equal/disp32 + 890 # . . discard args + 891 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 892 # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg) + 893 # . . push args + 894 68/push "F - test-convert-code-and-data-segments/1"/imm32 + 895 68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32 + 896 68/push _test-output-stream/imm32 + 897 # . . call + 898 e8/call check-next-stream-line-equal/disp32 + 899 # . . discard args + 900 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 901 # . check-next-stream-line-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg) + 902 # . . push args + 903 68/push "F - test-convert-code-and-data-segments/2"/imm32 + 904 68/push "68 20 # 68/push 0x20/imm8"/imm32 + 905 68/push _test-output-stream/imm32 + 906 # . . call + 907 e8/call check-next-stream-line-equal/disp32 + 908 # . . discard args + 909 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 910 # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) + 911 # . . push args + 912 68/push "F - test-convert-code-and-data-segments/3"/imm32 + 913 68/push "== data 0x2"/imm32 + 914 68/push _test-output-stream/imm32 + 915 # . . call + 916 e8/call check-next-stream-line-equal/disp32 + 917 # . . discard args + 918 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 919 # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) + 920 # . . push args + 921 68/push "F - test-convert-code-and-data-segments/4"/imm32 + 922 68/push "03 04 00 00 00 "/imm32 + 923 68/push _test-output-stream/imm32 + 924 # . . call + 925 e8/call check-next-stream-line-equal/disp32 + 926 # . . discard args + 927 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 928 # . epilog + 929 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 930 5d/pop-to-EBP + 931 c3/return + 932 + 933 convert-data: # line : (address stream byte), out : (address buffered-file) -> <void> + 934 # pseudocode: + 935 # var word-slice = {0, 0} + 936 # while true + 937 # word-slice = next-word(line) + 938 # if slice-empty?(word-slice) # end of file (maybe including trailing whitespace) + 939 # break # skip emitting some whitespace + 940 # if slice-starts-with?(word-slice, "#") # comment + 941 # write-slice-buffered(out, word-slice) + 942 # break + 943 # if slice-ends-with?(word-slice, ":") # label + 944 # write-stream-data(out, line) + 945 # break + 946 # if has-metadata?(word-slice, "imm32") + 947 # emit(out, word-slice, 4) + 948 # # disp32 is not permitted in data segments, and anything else is only a byte long + 949 # else + 950 # emit(out, word-slice, 1) + 951 # + 952 # . prolog + 953 55/push-EBP + 954 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 955 # . save registers + 956 50/push-EAX + 957 51/push-ECX + 958 52/push-EDX + 959 # var word-slice/ECX = {0, 0} + 960 68/push 0/imm32/end + 961 68/push 0/imm32/start + 962 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 963 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- + 989 $convert-data:loop: + 990 # next-word(line, word-slice) + 991 # . . push args + 992 51/push-ECX + 993 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 994 # . . call + 995 e8/call next-word/disp32 + 996 # . . discard args + 997 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 998 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- +1040 $convert-data:check0: +1041 # if (slice-empty?(word-slice)) break +1042 # . EAX = slice-empty?(word-slice) +1043 # . . push args +1044 51/push-ECX +1045 # . . call +1046 e8/call slice-empty?/disp32 +1047 # . . discard args +1048 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1049 # . if (EAX != 0) break +1050 3d/compare-EAX-and 0/imm32 +1051 0f 85/jump-if-not-equal $convert-data:break/disp32 +1052 $convert-data:check-for-comment: +1053 # if (slice-starts-with?(word-slice, "#")) +1054 # . start/EDX = word-slice->start +1055 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX +1056 # . c/EAX = *start +1057 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +1058 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL +1059 # . if (EAX != '#') goto next check +1060 3d/compare-EAX-and 0x23/imm32/hash +1061 75/jump-if-not-equal $convert-data:check-for-label/disp8 +1062 $convert-data:comment: +1063 # write-slice-buffered(out, word-slice) +1064 # . . push args +1065 51/push-ECX +1066 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1067 # . . call +1068 e8/call write-slice-buffered/disp32 +1069 # . . discard args +1070 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1071 # break +1072 75/jump-if-not-equal $convert-data:break/disp8 +1073 $convert-data:check-for-label: +1074 # if (slice-ends-with?(word-slice, ":")) +1075 # . end/EDX = word-slice->end +1076 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX +1077 # . c/EAX = *(end-1) +1078 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +1079 8a/copy-byte 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/AL -1/disp8 . # copy byte at *ECX to AL +1080 # . if (EAX != ':') goto next check +1081 3d/compare-EAX-and 0x3a/imm32/colon +1082 75/jump-if-not-equal $convert-data:check-for-imm32/disp8 +1083 $convert-data:label: +1084 # write-stream-data(out, line) +1085 # . . push args +1086 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1087 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1088 # . . call +1089 e8/call write-stream-data/disp32 +1090 # . . discard args +1091 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1092 # break +1093 75/jump-if-not-equal $convert-data:break/disp8 +1094 $convert-data:check-for-imm32: +1095 # if (has-metadata?(word-slice, "imm32")) +1096 # . EAX = has-metadata?(ECX, "imm32") +1097 # . . push args +1098 68/push "imm32"/imm32 +1099 51/push-ECX +1100 # . . call +1101 e8/call has-metadata?/disp32 +1102 # . . discard args +1103 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1104 # . if (EAX == 0) process as a single byte +1105 3d/compare-EAX-and 0/imm32 +1106 74/jump-if-equal $convert-data:single-byte/disp8 +1107 $convert-data:imm32: +1108 # emit(out, word-slice, 4) +1109 # . . push args +1110 68/push 4/imm32 +1111 51/push-ECX +1112 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1113 # . . call +1114 e8/call emit/disp32 +1115 # . . discard args +1116 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1117 e9/jump $convert-data:loop/disp32 +1118 $convert-data:single-byte: +1119 # emit(out, word-slice, 1) +1120 # . . push args +1121 68/push 1/imm32 +1122 51/push-ECX +1123 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1124 # . . call +1125 e8/call emit/disp32 +1126 # . . discard args +1127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1128 e9/jump $convert-data:loop/disp32 +1129 $convert-data:break: +1130 $convert-data:end: +1131 # . reclaim locals +1132 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1133 # . restore registers +1134 5a/pop-to-EDX +1135 59/pop-to-ECX +1136 58/pop-to-EAX +1137 # . epilog +1138 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1139 5d/pop-to-EBP +1140 c3/return +1141 +1142 test-convert-data-passes-comments-through: +1143 # if a line starts with '#', pass it along unchanged +1144 # . prolog +1145 55/push-EBP +1146 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1147 # setup +1148 # . clear-stream(_test-input-stream) +1149 # . . push args +1150 68/push _test-input-stream/imm32 +1151 # . . call +1152 e8/call clear-stream/disp32 +1153 # . . discard args +1154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1155 # . clear-stream(_test-output-stream) +1156 # . . push args +1157 68/push _test-output-stream/imm32 +1158 # . . call +1159 e8/call clear-stream/disp32 +1160 # . . discard args +1161 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1162 # . clear-stream(_test-output-buffered-file+4) +1163 # . . push args +1164 b8/copy-to-EAX _test-output-buffered-file/imm32 +1165 05/add-to-EAX 4/imm32 +1166 50/push-EAX +1167 # . . call +1168 e8/call clear-stream/disp32 +1169 # . . discard args +1170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1171 # initialize input +1172 # . write(_test-input-stream, "# abcd") +1173 # . . push args +1174 68/push "# abcd"/imm32 +1175 68/push _test-input-stream/imm32 +1176 # . . call +1177 e8/call write/disp32 +1178 # . . discard args +1179 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1180 # convert-data(_test-input-stream, _test-output-buffered-file) +1181 # . . push args +1182 68/push _test-output-buffered-file/imm32 +1183 68/push _test-input-stream/imm32 +1184 # . . call +1185 e8/call convert-data/disp32 +1186 # . . discard args +1187 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1188 # check that the line just passed through +1189 # . flush(_test-output-buffered-file) +1190 # . . push args +1191 68/push _test-output-buffered-file/imm32 +1192 # . . call +1193 e8/call flush/disp32 +1194 # . . discard args +1195 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1196 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1222 # . check-stream-equal(_test-output-stream, "# abcd", msg) +1223 # . . push args +1224 68/push "F - test-convert-data-passes-comments-through"/imm32 +1225 68/push "# abcd"/imm32 +1226 68/push _test-output-stream/imm32 +1227 # . . call +1228 e8/call check-stream-equal/disp32 +1229 # . . discard args +1230 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1231 # . epilog +1232 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1233 5d/pop-to-EBP +1234 c3/return +1235 +1236 test-convert-data-passes-labels-through: +1237 # if the first word ends with ':', pass along the entire line unchanged +1238 # . prolog +1239 55/push-EBP +1240 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1241 # setup +1242 # . clear-stream(_test-input-stream) +1243 # . . push args +1244 68/push _test-input-stream/imm32 +1245 # . . call +1246 e8/call clear-stream/disp32 +1247 # . . discard args +1248 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1249 # . clear-stream(_test-output-stream) +1250 # . . push args +1251 68/push _test-output-stream/imm32 +1252 # . . call +1253 e8/call clear-stream/disp32 +1254 # . . discard args +1255 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1256 # . clear-stream(_test-output-buffered-file+4) +1257 # . . push args +1258 b8/copy-to-EAX _test-output-buffered-file/imm32 +1259 05/add-to-EAX 4/imm32 +1260 50/push-EAX +1261 # . . call +1262 e8/call clear-stream/disp32 +1263 # . . discard args +1264 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1265 # initialize input +1266 # . write(_test-input-stream, "ab: # cd") +1267 # . . push args +1268 68/push "ab: # cd"/imm32 +1269 68/push _test-input-stream/imm32 +1270 # . . call +1271 e8/call write/disp32 +1272 # . . discard args +1273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1274 # convert-data(_test-input-stream, _test-output-buffered-file) +1275 # . . push args +1276 68/push _test-output-buffered-file/imm32 +1277 68/push _test-input-stream/imm32 +1278 # . . call +1279 e8/call convert-data/disp32 +1280 # . . discard args +1281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1282 # check that the line just passed through +1283 # . flush(_test-output-buffered-file) +1284 # . . push args +1285 68/push _test-output-buffered-file/imm32 +1286 # . . call +1287 e8/call flush/disp32 +1288 # . . discard args +1289 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1290 # . check-stream-equal(_test-output-stream, "ab: # cd", msg) +1291 # . . push args +1292 68/push "F - test-convert-data-passes-labels-through"/imm32 +1293 68/push "ab: # cd"/imm32 +1294 68/push _test-output-stream/imm32 +1295 # . . call +1296 e8/call check-stream-equal/disp32 +1297 # . . discard args +1298 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1299 # . epilog +1300 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1301 5d/pop-to-EBP +1302 c3/return +1303 +1304 test-convert-data-passes-names-through: +1305 # If a word is a valid name, just emit it unchanged. +1306 # Later phases will deal with it. +1307 # . prolog +1308 55/push-EBP +1309 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1310 # setup +1311 # . clear-stream(_test-input-stream) +1312 # . . push args +1313 68/push _test-input-stream/imm32 +1314 # . . call +1315 e8/call clear-stream/disp32 +1316 # . . discard args +1317 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1318 # . clear-stream(_test-output-stream) +1319 # . . push args +1320 68/push _test-output-stream/imm32 +1321 # . . call +1322 e8/call clear-stream/disp32 +1323 # . . discard args +1324 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1325 # . clear-stream(_test-output-buffered-file+4) +1326 # . . push args +1327 b8/copy-to-EAX _test-output-buffered-file/imm32 +1328 05/add-to-EAX 4/imm32 +1329 50/push-EAX +1330 # . . call +1331 e8/call clear-stream/disp32 +1332 # . . discard args +1333 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1334 # initialize input +1335 # . write(_test-input-stream, "abcd/imm32") +1336 # . . push args +1337 68/push "abcd/imm32"/imm32 +1338 68/push _test-input-stream/imm32 +1339 # . . call +1340 e8/call write/disp32 +1341 # . . discard args +1342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1343 # convert-data(_test-input-stream, _test-output-buffered-file) +1344 # . . push args +1345 68/push _test-output-buffered-file/imm32 +1346 68/push _test-input-stream/imm32 +1347 # . . call +1348 e8/call convert-data/disp32 +1349 # . . discard args +1350 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1351 # check that the line just passed through +1352 # . flush(_test-output-buffered-file) +1353 # . . push args +1354 68/push _test-output-buffered-file/imm32 +1355 # . . call +1356 e8/call flush/disp32 +1357 # . . discard args +1358 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1359 # . check-stream-equal(_test-output-stream, "abcd/imm32", msg) +1360 # . . push args +1361 68/push "F - test-convert-data-passes-names-through"/imm32 +1362 68/push "abcd/imm32 "/imm32 +1363 68/push _test-output-stream/imm32 +1364 # . . call +1365 e8/call check-stream-equal/disp32 +1366 # . . discard args +1367 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1368 # . epilog +1369 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1370 5d/pop-to-EBP +1371 c3/return +1372 +1373 test-convert-data-handles-imm32: +1374 # If a word has the /imm32 metadata, emit it in 4 bytes. +1375 # . prolog +1376 55/push-EBP +1377 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1378 # setup +1379 # . clear-stream(_test-input-stream) +1380 # . . push args +1381 68/push _test-input-stream/imm32 +1382 # . . call +1383 e8/call clear-stream/disp32 +1384 # . . discard args +1385 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1386 # . clear-stream(_test-output-stream) +1387 # . . push args +1388 68/push _test-output-stream/imm32 +1389 # . . call +1390 e8/call clear-stream/disp32 +1391 # . . discard args +1392 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1393 # . clear-stream(_test-output-buffered-file+4) +1394 # . . push args +1395 b8/copy-to-EAX _test-output-buffered-file/imm32 +1396 05/add-to-EAX 4/imm32 +1397 50/push-EAX +1398 # . . call +1399 e8/call clear-stream/disp32 +1400 # . . discard args +1401 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1402 # initialize input +1403 # . write(_test-input-stream, "30/imm32") +1404 # . . push args +1405 68/push "30/imm32"/imm32 +1406 68/push _test-input-stream/imm32 +1407 # . . call +1408 e8/call write/disp32 +1409 # . . discard args +1410 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1411 # convert-data(_test-input-stream, _test-output-buffered-file) +1412 # . . push args +1413 68/push _test-output-buffered-file/imm32 +1414 68/push _test-input-stream/imm32 +1415 # . . call +1416 e8/call convert-data/disp32 +1417 # . . discard args +1418 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1419 # check that 4 bytes were written +1420 # . flush(_test-output-buffered-file) +1421 # . . push args +1422 68/push _test-output-buffered-file/imm32 +1423 # . . call +1424 e8/call flush/disp32 +1425 # . . discard args +1426 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1427 # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg) +1428 # . . push args +1429 68/push "F - test-convert-data-handles-imm32"/imm32 +1430 68/push "30 00 00 00 "/imm32 +1431 68/push _test-output-stream/imm32 +1432 # . . call +1433 e8/call check-stream-equal/disp32 +1434 # . . discard args +1435 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1436 # . epilog +1437 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1438 5d/pop-to-EBP +1439 c3/return +1440 +1441 test-convert-data-handles-single-byte: +1442 # Any metadata but /imm32 will emit a single byte. +1443 # Data segments can't have /disp32, and SubX doesn't support 16-bit operands. +1444 # . prolog +1445 55/push-EBP +1446 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1447 # setup +1448 # . clear-stream(_test-input-stream) +1449 # . . push args +1450 68/push _test-input-stream/imm32 +1451 # . . call +1452 e8/call clear-stream/disp32 +1453 # . . discard args +1454 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1455 # . clear-stream(_test-output-stream) +1456 # . . push args +1457 68/push _test-output-stream/imm32 +1458 # . . call +1459 e8/call clear-stream/disp32 +1460 # . . discard args +1461 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1462 # . clear-stream(_test-output-buffered-file+4) +1463 # . . push args +1464 b8/copy-to-EAX _test-output-buffered-file/imm32 +1465 05/add-to-EAX 4/imm32 +1466 50/push-EAX +1467 # . . call +1468 e8/call clear-stream/disp32 +1469 # . . discard args +1470 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1471 # initialize input +1472 # . write(_test-input-stream, "30/imm16") +1473 # . . push args +1474 68/push "30/imm16"/imm32 +1475 68/push _test-input-stream/imm32 +1476 # . . call +1477 e8/call write/disp32 +1478 # . . discard args +1479 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1480 # convert-data(_test-input-stream, _test-output-buffered-file) +1481 # . . push args +1482 68/push _test-output-buffered-file/imm32 +1483 68/push _test-input-stream/imm32 +1484 # . . call +1485 e8/call convert-data/disp32 +1486 # . . discard args +1487 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1488 # check that a single byte was written (imm16 is not a valid operand type) +1489 # . flush(_test-output-buffered-file) +1490 # . . push args +1491 68/push _test-output-buffered-file/imm32 +1492 # . . call +1493 e8/call flush/disp32 +1494 # . . discard args +1495 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1496 # . check-stream-equal(_test-output-stream, "30 ", msg) +1497 # . . push args +1498 68/push "F - test-convert-data-handles-single-byte"/imm32 +1499 68/push "30 "/imm32 +1500 68/push _test-output-stream/imm32 +1501 # . . call +1502 e8/call check-stream-equal/disp32 +1503 # . . discard args +1504 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1505 # . epilog +1506 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1507 5d/pop-to-EBP +1508 c3/return +1509 +1510 test-convert-data-multiple-bytes: +1511 # Multiple single-byte words in input stream get processed one by one. +1512 # . prolog +1513 55/push-EBP +1514 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1515 # setup +1516 # . clear-stream(_test-input-stream) +1517 # . . push args +1518 68/push _test-input-stream/imm32 +1519 # . . call +1520 e8/call clear-stream/disp32 +1521 # . . discard args +1522 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1523 # . clear-stream(_test-output-stream) +1524 # . . push args +1525 68/push _test-output-stream/imm32 +1526 # . . call +1527 e8/call clear-stream/disp32 +1528 # . . discard args +1529 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1530 # . clear-stream(_test-output-buffered-file+4) +1531 # . . push args +1532 b8/copy-to-EAX _test-output-buffered-file/imm32 +1533 05/add-to-EAX 4/imm32 +1534 50/push-EAX +1535 # . . call +1536 e8/call clear-stream/disp32 +1537 # . . discard args +1538 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1539 # initialize input +1540 # . write(_test-input-stream, "1 2") +1541 # . . push args +1542 68/push "1 2"/imm32 +1543 68/push _test-input-stream/imm32 +1544 # . . call +1545 e8/call write/disp32 +1546 # . . discard args +1547 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1548 # convert-data(_test-input-stream, _test-output-buffered-file) +1549 # . . push args +1550 68/push _test-output-buffered-file/imm32 +1551 68/push _test-input-stream/imm32 +1552 # . . call +1553 e8/call convert-data/disp32 +1554 # . . discard args +1555 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1556 # check output +1557 # . flush(_test-output-buffered-file) +1558 # . . push args +1559 68/push _test-output-buffered-file/imm32 +1560 # . . call +1561 e8/call flush/disp32 +1562 # . . discard args +1563 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1564 # . check-stream-equal(_test-output-stream, "01 02 ", msg) +1565 # . . push args +1566 68/push "F - test-convert-data-multiple-bytes"/imm32 +1567 68/push "01 02 "/imm32 +1568 68/push _test-output-stream/imm32 +1569 # . . call +1570 e8/call check-stream-equal/disp32 +1571 # . . discard args +1572 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1573 # . epilog +1574 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1575 5d/pop-to-EBP +1576 c3/return +1577 +1578 test-convert-data-byte-then-name: +1579 # Single-byte word followed by valid name get processed one by one. +1580 # . prolog +1581 55/push-EBP +1582 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1583 # setup +1584 # . clear-stream(_test-input-stream) +1585 # . . push args +1586 68/push _test-input-stream/imm32 +1587 # . . call +1588 e8/call clear-stream/disp32 +1589 # . . discard args +1590 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1591 # . clear-stream(_test-output-stream) +1592 # . . push args +1593 68/push _test-output-stream/imm32 +1594 # . . call +1595 e8/call clear-stream/disp32 +1596 # . . discard args +1597 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1598 # . clear-stream(_test-output-buffered-file+4) +1599 # . . push args +1600 b8/copy-to-EAX _test-output-buffered-file/imm32 +1601 05/add-to-EAX 4/imm32 +1602 50/push-EAX +1603 # . . call +1604 e8/call clear-stream/disp32 +1605 # . . discard args +1606 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1607 # initialize input +1608 # . write(_test-input-stream, "30 abcd/o") +1609 # . . push args +1610 68/push "30 abcd/o"/imm32 +1611 68/push _test-input-stream/imm32 +1612 # . . call +1613 e8/call write/disp32 +1614 # . . discard args +1615 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1616 # convert-data(_test-input-stream, _test-output-buffered-file) +1617 # . . push args +1618 68/push _test-output-buffered-file/imm32 +1619 68/push _test-input-stream/imm32 +1620 # . . call +1621 e8/call convert-data/disp32 +1622 # . . discard args +1623 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1624 # check output +1625 # . flush(_test-output-buffered-file) +1626 # . . push args +1627 68/push _test-output-buffered-file/imm32 +1628 # . . call +1629 e8/call flush/disp32 +1630 # . . discard args +1631 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1632 # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg) +1633 # . . push args +1634 68/push "F - test-convert-data-byte-then-name"/imm32 +1635 68/push "30 abcd/o "/imm32 +1636 68/push _test-output-stream/imm32 +1637 # . . call +1638 e8/call check-stream-equal/disp32 +1639 # . . discard args +1640 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1641 # . epilog +1642 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1643 5d/pop-to-EBP +1644 c3/return +1645 +1646 test-convert-data-multiple-words: +1647 # Multiple words in input stream get processed one by one. +1648 # . prolog +1649 55/push-EBP +1650 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1651 # setup +1652 # . clear-stream(_test-input-stream) +1653 # . . push args +1654 68/push _test-input-stream/imm32 +1655 # . . call +1656 e8/call clear-stream/disp32 +1657 # . . discard args +1658 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1659 # . clear-stream(_test-output-stream) +1660 # . . push args +1661 68/push _test-output-stream/imm32 +1662 # . . call +1663 e8/call clear-stream/disp32 +1664 # . . discard args +1665 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1666 # . clear-stream(_test-output-buffered-file+4) +1667 # . . push args +1668 b8/copy-to-EAX _test-output-buffered-file/imm32 +1669 05/add-to-EAX 4/imm32 +1670 50/push-EAX +1671 # . . call +1672 e8/call clear-stream/disp32 +1673 # . . discard args +1674 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1675 # initialize input +1676 # . write(_test-input-stream, "30 abcd/o 42e1/imm32") +1677 # . . push args +1678 68/push "30 abcd/o 42e1/imm32"/imm32 +1679 68/push _test-input-stream/imm32 +1680 # . . call +1681 e8/call write/disp32 +1682 # . . discard args +1683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1684 # convert-data(_test-input-stream, _test-output-buffered-file) +1685 # . . push args +1686 68/push _test-output-buffered-file/imm32 +1687 68/push _test-input-stream/imm32 +1688 # . . call +1689 e8/call convert-data/disp32 +1690 # . . discard args +1691 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1692 # check output +1693 # . flush(_test-output-buffered-file) +1694 # . . push args +1695 68/push _test-output-buffered-file/imm32 +1696 # . . call +1697 e8/call flush/disp32 +1698 # . . discard args +1699 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1700 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1726 # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg) +1727 # . . push args +1728 68/push "F - test-convert-data-multiple-words"/imm32 +1729 68/push "30 abcd/o e1 42 00 00 "/imm32 +1730 68/push _test-output-stream/imm32 +1731 # . . call +1732 e8/call check-stream-equal/disp32 +1733 # . . discard args +1734 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1735 # . epilog +1736 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1737 5d/pop-to-EBP +1738 c3/return +1739 +1740 test-convert-data-trailing-comment: +1741 # Trailing comments in data segment get appropriately ignored. +1742 # . prolog +1743 55/push-EBP +1744 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1745 # setup +1746 # . clear-stream(_test-input-stream) +1747 # . . push args +1748 68/push _test-input-stream/imm32 +1749 # . . call +1750 e8/call clear-stream/disp32 +1751 # . . discard args +1752 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1753 # . clear-stream(_test-output-stream) +1754 # . . push args +1755 68/push _test-output-stream/imm32 +1756 # . . call +1757 e8/call clear-stream/disp32 +1758 # . . discard args +1759 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1760 # . clear-stream(_test-output-buffered-file+4) +1761 # . . push args +1762 b8/copy-to-EAX _test-output-buffered-file/imm32 +1763 05/add-to-EAX 4/imm32 +1764 50/push-EAX +1765 # . . call +1766 e8/call clear-stream/disp32 +1767 # . . discard args +1768 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1769 # initialize input +1770 # . write(_test-input-stream, "30/imm32 # comment") +1771 # . . push args +1772 68/push "30/imm32 # comment"/imm32 +1773 68/push _test-input-stream/imm32 +1774 # . . call +1775 e8/call write/disp32 +1776 # . . discard args +1777 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1778 # convert-data(_test-input-stream, _test-output-buffered-file) +1779 # . . push args +1780 68/push _test-output-buffered-file/imm32 +1781 68/push _test-input-stream/imm32 +1782 # . . call +1783 e8/call convert-data/disp32 +1784 # . . discard args +1785 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1786 # check output +1787 # . flush(_test-output-buffered-file) +1788 # . . push args +1789 68/push _test-output-buffered-file/imm32 +1790 # . . call +1791 e8/call flush/disp32 +1792 # . . discard args +1793 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1794 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1820 # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg) +1821 # . . push args +1822 68/push "F - test-convert-data-trailing-comment"/imm32 +1823 68/push "30 00 00 00 # comment"/imm32 +1824 68/push _test-output-stream/imm32 +1825 # . . call +1826 e8/call check-stream-equal/disp32 +1827 # . . discard args +1828 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1829 # . epilog +1830 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1831 5d/pop-to-EBP +1832 c3/return +1833 +1834 # pack an instruction, following the C++ version +1835 # +1836 # zero error handling at the moment (continuing to rely on the C++ version for that): +1837 # missing fields are always 0-filled +1838 # bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte. +1839 # may pick up any of duplicate operands in an instruction +1840 # silently drop extraneous operands +1841 # unceremoniously abort on non-numeric operands except disp or imm +1842 # opcodes must be lowercase and zero padded +1843 # opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this. +1844 convert-instruction: # line : (address stream byte), out : (address buffered-file) -> <void> +1845 # pseudocode: +1846 # # some early exits +1847 # var word-slice = next-word(line) +1848 # if slice-empty?(word-slice) +1849 # write-stream-data(out, line) +1850 # return +1851 # if slice-starts-with?(word-slice, "#") +1852 # write-stream-data(out, line) +1853 # return +1854 # if slice-ends-with?(word-slice, ":") +1855 # write-stream-data(out, line) +1856 # return +1857 # # really convert +1858 # emit-opcodes(line, out) +1859 # emit-modrm(line, out) +1860 # emit-sib(line, out) +1861 # emit-disp(line, out) +1862 # emit-imm(line, out) +1863 # emit-line-in-comment(line, out) +1864 # +1865 # . prolog +1866 55/push-EBP +1867 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1868 # . save registers +1869 50/push-EAX +1870 51/push-ECX +1871 52/push-EDX +1872 # var word-slice/ECX = {0, 0} +1873 68/push 0/imm32/end +1874 68/push 0/imm32/start +1875 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1876 # next-word(line, word-slice) +1877 # . . push args +1878 51/push-ECX +1879 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1880 # . . call +1881 e8/call next-word/disp32 +1882 # . . discard args +1883 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1884 $convert-instruction:check0: +1885 # if (slice-empty?(word-slice)) break +1886 # . EAX = slice-empty?(word-slice) +1887 # . . push args +1888 51/push-ECX +1889 # . . call +1890 e8/call slice-empty?/disp32 +1891 # . . discard args +1892 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1893 # . if (EAX != 0) pass through +1894 3d/compare-EAX-and 0/imm32 +1895 75/jump-if-not-equal $convert-instruction:pass-through/disp8 +1896 $convert-instruction:check1: +1897 # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line) +1898 # . start/EDX = word-slice->start +1899 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX +1900 # . c/EAX = *start +1901 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +1902 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL +1903 # . if (EAX == '#') pass through +1904 3d/compare-EAX-and 0x23/imm32/hash +1905 74/jump-if-equal $convert-instruction:pass-through/disp8 +1906 $convert-instruction:check2: +1907 # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line) +1908 # . end/EDX = word-slice->end +1909 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX +1910 # . c/EAX = *(end-1) +1911 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +1912 8a/copy-byte 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/AL -1/disp8 . # copy byte at *ECX to AL +1913 # . if (EAX == ':') pass through +1914 3d/compare-EAX-and 0x3a/imm32/colon +1915 75/jump-if-not-equal $convert-instruction:really-convert/disp8 +1916 $convert-instruction:pass-through: +1917 # write-stream-data(out, line) +1918 # . . push args +1919 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1920 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1921 # . . call +1922 e8/call write-stream-data/disp32 +1923 # . . discard args +1924 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1925 # return +1926 eb/jump $convert-instruction:end/disp8 +1927 $convert-instruction:really-convert: +1928 # emit-opcodes(line, out) +1929 # . . push args +1930 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1931 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1932 # . . call +1933 e8/call emit-opcodes/disp32 +1934 # . . discard args +1935 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1936 # emit-modrm(line, out) +1937 # . . push args +1938 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1939 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1940 # . . call +1941 e8/call emit-modrm/disp32 +1942 # . . discard args +1943 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1944 # emit-sib(line, out) +1945 # . . push args +1946 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1947 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1948 # . . call +1949 e8/call emit-sib/disp32 +1950 # . . discard args +1951 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1952 # emit-disp(line, out) +1953 # . . push args +1954 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1955 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1956 # . . call +1957 e8/call emit-disp/disp32 +1958 # . . discard args +1959 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1960 # emit-imm(line, out) +1961 # . . push args +1962 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1963 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1964 # . . call +1965 e8/call emit-imm/disp32 +1966 # . . discard args +1967 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1968 # emit-line-in-comment(line, out) +1969 # . . push args +1970 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +1971 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1972 # . . call +1973 e8/call emit-line-in-comment/disp32 +1974 # . . discard args +1975 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1976 $convert-instruction:end: +1977 # . restore locals +1978 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1979 # . restore registers +1980 5a/pop-to-EDX +1981 59/pop-to-ECX +1982 58/pop-to-EAX +1983 # . epilog +1984 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1985 5d/pop-to-EBP +1986 c3/return +1987 +1988 emit-opcodes: # line : (address stream byte), out : (address buffered-file) -> <void> +1989 # opcodes occupy 1-3 bytes: +1990 # xx +1991 # 0f xx +1992 # f2 xx +1993 # f3 xx +1994 # f2 0f xx +1995 # f3 0f xx +1996 # +1997 # pseudocode: +1998 # rewind-stream(line) +1999 # +2000 # var op1 = next-word(line) +2001 # if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return +2002 # op1 = next-token-from-slice(op1->start, op1->end, "/") +2003 # write-slice-buffered(out, op1) +2004 # if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3") +2005 # return +2006 # +2007 # var op2 = next-word(line) +2008 # if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return +2009 # op2 = next-token-from-slice(op2->start, op2->end, "/") +2010 # write-slice-buffered(out, op2) +2011 # if slice-equal?(op1, "0f") +2012 # return +2013 # if !slice-equal?(op2, "0f") +2014 # return +2015 # +2016 # var op3 = next-word(line) +2017 # if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return +2018 # op3 = next-token-from-slice(op3->start, op3->end, "/") +2019 # write-slice-buffered(out, op3) +2020 # +2021 # . prolog +2022 55/push-EBP +2023 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2024 # . save registers +2025 50/push-EAX +2026 51/push-ECX +2027 52/push-EDX +2028 53/push-EBX +2029 # var op1/ECX = {0, 0} +2030 68/push 0/imm32/end +2031 68/push 0/imm32/start +2032 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2033 # var op2/EDX = {0, 0} +2034 68/push 0/imm32/end +2035 68/push 0/imm32/start +2036 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX +2037 # rewind-stream(line) +2038 # . . push args +2039 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2040 # . . call +2041 e8/call rewind-stream/disp32 +2042 # . . discard args +2043 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2044 $emit-opcodes:op1: +2045 # next-word(line, op1) +2046 # . . push args +2047 51/push-ECX +2048 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2049 # . . call +2050 e8/call next-word/disp32 +2051 # . . discard args +2052 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2053 # if (slice-empty?(op1)) return +2054 # . EAX = slice-empty?(op1) +2055 # . . push args +2056 51/push-ECX +2057 # . . call +2058 e8/call slice-empty?/disp32 +2059 # . . discard args +2060 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2061 # . if (EAX != 0) return +2062 3d/compare-EAX-and 0/imm32 +2063 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 +2064 # if (slice-starts-with?(op1, "#")) return +2065 # . start/EBX = op1->start +2066 8b/copy 0/mod/indirect 1/rm32/ECX . . . 3/r32/EBX . . # copy *ECX to EBX +2067 # . c/EAX = *start +2068 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2069 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL +2070 # . if (EAX == '#') return +2071 3d/compare-EAX-and 0x23/imm32/hash +2072 0f 84/jump-if-equal $emit-opcodes:end/disp32 +2073 # op1 = next-token-from-slice(op1->start, op1->end, '/') +2074 # . . push args +2075 51/push-ECX +2076 68/push 0x2f/imm32/slash +2077 ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) +2078 ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX +2079 # . . call +2080 e8/call next-token-from-slice/disp32 +2081 # . . discard args +2082 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +2083 # write-slice-buffered(out, op1) +2084 # . . push args +2085 51/push-ECX +2086 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2087 # . . call +2088 e8/call write-slice-buffered/disp32 +2089 # . . discard args +2090 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2091 # write-buffered(out, " ") +2092 # . . push args +2093 68/push " "/imm32 +2094 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2095 # . . call +2096 e8/call write-buffered/disp32 +2097 # . . discard args +2098 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2099 # if (slice-equal?(op1, "0f")) goto op2 +2100 # . EAX = slice-equal?(op1, "0f") +2101 # . . push args +2102 68/push "0f"/imm32 +2103 51/push-ECX +2104 # . . call +2105 e8/call slice-equal?/disp32 +2106 # . . discard args +2107 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2108 # . if (EAX != 0) goto op2 +2109 3d/compare-EAX-and 0/imm32 +2110 75/jump-if-not-equal $emit-opcodes:op2/disp8 +2111 # if (slice-equal?(op1, "f2")) goto op2 +2112 # . EAX = slice-equal?(op1, "f2") +2113 # . . push args +2114 68/push "f2"/imm32 +2115 51/push-ECX +2116 # . . call +2117 e8/call slice-equal?/disp32 +2118 # . . discard args +2119 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2120 # . if (EAX != 0) goto op2 +2121 3d/compare-EAX-and 0/imm32 +2122 75/jump-if-not-equal $emit-opcodes:op2/disp8 +2123 # if (slice-equal?(op1, "f3")) goto op2 +2124 # . EAX = slice-equal?(op1, "f3") +2125 # . . push args +2126 68/push "f3"/imm32 +2127 51/push-ECX +2128 # . . call +2129 e8/call slice-equal?/disp32 +2130 # . . discard args +2131 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2132 # . if (EAX != 0) goto op2 +2133 3d/compare-EAX-and 0/imm32 +2134 75/jump-if-not-equal $emit-opcodes:op2/disp8 +2135 # otherwise return +2136 e9/jump $emit-opcodes:end/disp32 +2137 $emit-opcodes:op2: +2138 # next-word(line, op2) +2139 # . . push args +2140 52/push-EDX +2141 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2142 # . . call +2143 e8/call next-word/disp32 +2144 # . . discard args +2145 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2146 # if (slice-empty?(op2)) return +2147 # . EAX = slice-empty?(op2) +2148 # . . push args +2149 52/push-EDX +2150 # . . call +2151 e8/call slice-empty?/disp32 +2152 # . . discard args +2153 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2154 # . if (EAX != 0) return +2155 3d/compare-EAX-and 0/imm32 +2156 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 +2157 # if (slice-starts-with?(op2, "#")) return +2158 # . start/EBX = op2->start +2159 8b/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy *EDX to EBX +2160 # . c/EAX = *start +2161 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2162 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL +2163 # . if (EAX == '#') return +2164 3d/compare-EAX-and 0x23/imm32/hash +2165 0f 84/jump-if-equal $emit-opcodes:end/disp32 +2166 # op2 = next-token-from-slice(op2->start, op2->end, '/') +2167 # . . push args +2168 52/push-EDX +2169 68/push 0x2f/imm32/slash +2170 ff 6/subop/push 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # push *(EDX+4) +2171 ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX +2172 # . . call +2173 e8/call next-token-from-slice/disp32 +2174 # . . discard args +2175 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +2176 # write-slice-buffered(out, op2) +2177 # . . push args +2178 52/push-EDX +2179 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2180 # . . call +2181 e8/call write-slice-buffered/disp32 +2182 # . . discard args +2183 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2184 # write-buffered(out, " ") +2185 # . . push args +2186 68/push " "/imm32 +2187 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2188 # . . call +2189 e8/call write-buffered/disp32 +2190 # . . discard args +2191 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2192 # if (slice-equal?(op1, "0f")) return +2193 # . EAX = slice-equal?(op1, "0f") +2194 # . . push args +2195 68/push "0f"/imm32 +2196 51/push-ECX +2197 # . . call +2198 e8/call slice-equal?/disp32 +2199 # . . discard args +2200 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2201 # . if (EAX != 0) return +2202 3d/compare-EAX-and 0/imm32 +2203 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 +2204 # if (!slice-equal?(op2, "0f")) return +2205 # . EAX = slice-equal?(op2, "0f") +2206 # . . push args +2207 68/push "0f"/imm32 +2208 52/push-EDX +2209 # . . call +2210 e8/call slice-equal?/disp32 +2211 # . . discard args +2212 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2213 # . if (EAX == 0) return +2214 3d/compare-EAX-and 0/imm32 +2215 0f 84/jump-if-equal $emit-opcodes:end/disp32 +2216 $emit-opcodes:op3: +2217 # next-word(line, op3) # reuse op2/EDX +2218 # . . push args +2219 52/push-EDX +2220 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2221 # . . call +2222 e8/call next-word/disp32 +2223 # . . discard args +2224 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2225 # if (slice-empty?(op3)) return +2226 # . EAX = slice-empty?(op3) +2227 # . . push args +2228 52/push-EDX +2229 # . . call +2230 e8/call slice-empty?/disp32 +2231 # . . discard args +2232 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2233 # . if (EAX != 0) return +2234 3d/compare-EAX-and 0/imm32 +2235 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 +2236 # if (slice-starts-with?(op3, "#")) return +2237 # . start/EBX = op2->start +2238 8b/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy *EDX to EBX +2239 # . c/EAX = *start +2240 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2241 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL +2242 # . if (EAX == '#') return +2243 3d/compare-EAX-and 0x23/imm32/hash +2244 0f 84/jump-if-equal $emit-opcodes:end/disp32 +2245 # op3 = next-token-from-slice(op3->start, op3->end, '/') +2246 # . . push args +2247 52/push-EDX +2248 68/push 0x2f/imm32/slash +2249 ff 6/subop/push 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # push *(EDX+4) +2250 ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX +2251 # . . call +2252 e8/call next-token-from-slice/disp32 +2253 # . . discard args +2254 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +2255 # write-slice-buffered(out, op3) +2256 # . . push args +2257 52/push-EDX +2258 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2259 # . . call +2260 e8/call write-slice-buffered/disp32 +2261 # . . discard args +2262 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2263 # write-buffered(out, " ") +2264 # . . push args +2265 68/push " "/imm32 +2266 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2267 # . . call +2268 e8/call write-buffered/disp32 +2269 # . . discard args +2270 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2271 $emit-opcodes:end: +2272 # . restore locals +2273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +2274 # . restore registers +2275 5b/pop-to-EBX +2276 5a/pop-to-EDX +2277 59/pop-to-ECX +2278 58/pop-to-EAX +2279 # . epilog +2280 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2281 5d/pop-to-EBP +2282 c3/return +2283 +2284 emit-modrm: # line : (address stream byte), out : (address buffered-file) -> <void> +2285 # pseudocode: +2286 # rewind-stream(line) +2287 # var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0 +2288 # var word-slice = {0, 0} +2289 # while true +2290 # word-slice = next-word(line) +2291 # if (slice-empty?(word-slice)) break +2292 # if (slice-starts-with?(word-slice, "#")) break +2293 # if (has-metadata?(word-slice, "mod")) +2294 # mod = parse-hex-int(next-token-from-slice(word-slice, "/")) +2295 # has-modrm? = true +2296 # else if (has-metadata?(word-slice, "rm32")) +2297 # rm32 = parse-hex-int(next-token-from-slice(word-slice, "/")) +2298 # has-modrm? = true +2299 # else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop")) +2300 # r32 = parse-hex-int(next-token-from-slice(word-slice, "/")) +2301 # has-modrm? = true +2302 # if has-modrm? +2303 # var modrm = mod & 0b11 +2304 # modrm <<= 2 +2305 # modrm |= r32 & 0b111 +2306 # modrm <<= 3 +2307 # modrm |= rm32 & 0b111 +2308 # emit-hex(out, modrm, 1) +2309 # +2310 # . prolog +2311 55/push-EBP +2312 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2313 # . save registers +2314 50/push-EAX +2315 51/push-ECX +2316 52/push-EDX +2317 53/push-EBX +2318 56/push-ESI +2319 57/push-EDI +2320 # var word-slice/ECX = {0, 0} +2321 68/push 0/imm32/end +2322 68/push 0/imm32/start +2323 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2324 # var has-modrm?/EDX = false +2325 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX +2326 # var mod/EBX = 0 +2327 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX +2328 # var rm32/ESI = 0 +2329 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI +2330 # var r32/EDI = 0 +2331 31/xor 3/mod/direct 7/rm32/EDI . . . 7/r32/EDI . . # clear EDI +2332 # rewind-stream(line) +2333 # . . push args +2334 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2335 # . . call +2336 e8/call rewind-stream/disp32 +2337 # . . discard args +2338 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2339 $emit-modrm:loop: +2340 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- +2366 # next-word(line, word-slice) +2367 # . . push args +2368 51/push-ECX +2369 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2370 # . . call +2371 e8/call next-word/disp32 +2372 # . . discard args +2373 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2374 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- +2416 $emit-modrm:check0: +2417 # if (slice-empty?(word-slice)) break +2418 # . EAX = slice-empty?(word-slice) +2419 # . . push args +2420 51/push-ECX +2421 # . . call +2422 e8/call slice-empty?/disp32 +2423 # . . discard args +2424 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2425 # . if (EAX != 0) pass through +2426 3d/compare-EAX-and 0/imm32 +2427 0f 85/jump-if-not-equal $emit-modrm:break/disp32 +2428 $emit-modrm:check1: +2429 # if (slice-starts-with?(word-slice, "#")) break +2430 # . spill EDX +2431 52/push-EDX +2432 # . start/EDX = word-slice->start +2433 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX +2434 # . c/EAX = *start +2435 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2436 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL +2437 # . restore EDX +2438 5a/pop-to-EDX +2439 # . if (EAX == '#') pass through +2440 3d/compare-EAX-and 0x23/imm32/hash +2441 0f 84/jump-if-equal $emit-modrm:break/disp32 +2442 $emit-modrm:check-for-mod: +2443 # if (has-metadata?(word-slice, "mod")) +2444 # . EAX = has-metadata?(ECX, "mod") +2445 # . . push args +2446 68/push "mod"/imm32 +2447 51/push-ECX +2448 # . . call +2449 e8/call has-metadata?/disp32 +2450 # . . discard args +2451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2452 # . if (EAX == 0) goto next check +2453 3d/compare-EAX-and 0/imm32 +2454 74/jump-if-equal $emit-modrm:check-for-rm32/disp8 +2455 $emit-modrm:mod: +2456 # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) +2457 # . EAX = parse-datum-of-word(word-slice) +2458 # . . push args +2459 51/push-ECX +2460 # . . call +2461 e8/call parse-datum-of-word/disp32 +2462 # . . discard args +2463 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2464 # . mod = EAX +2465 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX +2466 # has-modrm? = true +2467 ba/copy-to-EDX 1/imm32/true +2468 # continue +2469 e9/jump $emit-modrm:loop/disp32 +2470 $emit-modrm:check-for-rm32: +2471 # if (has-metadata?(word-slice, "rm32")) +2472 # . EAX = has-metadata?(ECX, "rm32") +2473 # . . push args +2474 68/push "rm32"/imm32 +2475 51/push-ECX +2476 # . . call +2477 e8/call has-metadata?/disp32 +2478 # . . discard args +2479 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2480 # . if (EAX == 0) goto next check +2481 3d/compare-EAX-and 0/imm32 +2482 74/jump-if-equal $emit-modrm:check-for-r32/disp8 +2483 $emit-modrm:rm32: +2484 # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) +2485 # . EAX = parse-datum-of-word(word-slice) +2486 # . . push args +2487 51/push-ECX +2488 # . . call +2489 e8/call parse-datum-of-word/disp32 +2490 # . . discard args +2491 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2492 # . rm32 = EAX +2493 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI +2494 # has-modrm? = true +2495 ba/copy-to-EDX 1/imm32/true +2496 # continue +2497 e9/jump $emit-modrm:loop/disp32 +2498 $emit-modrm:check-for-r32: +2499 # if (has-metadata?(word-slice, "r32")) +2500 # . EAX = has-metadata?(ECX, "r32") +2501 # . . push args +2502 68/push "r32"/imm32 +2503 51/push-ECX +2504 # . . call +2505 e8/call has-metadata?/disp32 +2506 # . . discard args +2507 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2508 # . if (EAX == 0) goto next check +2509 3d/compare-EAX-and 0/imm32 +2510 74/jump-if-equal $emit-modrm:check-for-subop/disp8 +2511 $emit-modrm:r32: +2512 # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) +2513 # . EAX = parse-datum-of-word(word-slice) +2514 # . . push args +2515 51/push-ECX +2516 # . . call +2517 e8/call parse-datum-of-word/disp32 +2518 # . . discard args +2519 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2520 # . r32 = EAX +2521 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI +2522 # has-modrm? = true +2523 ba/copy-to-EDX 1/imm32/true +2524 # continue +2525 e9/jump $emit-modrm:loop/disp32 +2526 $emit-modrm:check-for-subop: +2527 # if (has-metadata?(word-slice, "subop")) +2528 # . EAX = has-metadata?(ECX, "subop") +2529 # . . push args +2530 68/push "subop"/imm32 +2531 51/push-ECX +2532 # . . call +2533 e8/call has-metadata?/disp32 +2534 # . . discard args +2535 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2536 # . if (EAX == 0) loop +2537 3d/compare-EAX-and 0/imm32 +2538 0f 84/jump-if-equal $emit-modrm:loop/disp32 +2539 $emit-modrm:subop: +2540 # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) +2541 # . EAX = parse-datum-of-word(word-slice) +2542 # . . push args +2543 51/push-ECX +2544 # . . call +2545 e8/call parse-datum-of-word/disp32 +2546 # . . discard args +2547 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2548 # . r32 = EAX +2549 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI +2550 # has-modrm? = true +2551 ba/copy-to-EDX 1/imm32/true +2552 # continue +2553 e9/jump $emit-modrm:loop/disp32 +2554 $emit-modrm:break: +2555 # if (!has-modrm?) return +2556 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX +2557 74/jump-if-equal $emit-modrm:end/disp8 +2558 $emit-modrm:calculate: +2559 # modrm/EBX = mod & 0b11 +2560 81 4/subop/and 3/mod/direct 3/rm32/EBX . . . . . 3/imm32/0b11 # bitwise and of EBX +2561 # modrm <<= 2 +2562 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 2/imm8 # shift EBX left by 2 bits +2563 # modrm |= r32 & 0b111 +2564 81 4/subop/and 3/mod/direct 7/rm32/EDI . . . . . 7/imm32/0b111 # bitwise and of EDI +2565 09/or 3/mod/direct 3/rm32/EBX . . . 7/r32/EDI . . # EBX = bitwise OR with EDI +2566 # modrm <<= 3 +2567 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 3/imm8 # shift EBX left by 3 bits +2568 # modrm |= rm32 & 0b111 +2569 81 4/subop/and 3/mod/direct 6/rm32/ESI . . . . . 7/imm32/0b111 # bitwise and of ESI +2570 09/or 3/mod/direct 3/rm32/EBX . . . 6/r32/ESI . . # EBX = bitwise OR with ESI +2571 $emit-modrm:emit: +2572 # emit-hex(out, modrm, 1) +2573 # . . push args +2574 68/push 1/imm32 +2575 53/push-EBX +2576 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2577 # . . call +2578 e8/call emit-hex/disp32 +2579 # . . discard args +2580 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2581 $emit-modrm:end: +2582 # . restore locals +2583 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2584 # . restore registers +2585 5f/pop-to-EDI +2586 5e/pop-to-ESI +2587 5b/pop-to-EBX +2588 5a/pop-to-EDX +2589 59/pop-to-ECX +2590 58/pop-to-EAX +2591 # . epilog +2592 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2593 5d/pop-to-EBP +2594 c3/return +2595 +2596 emit-sib: # line : (address stream byte), out : (address buffered-file) -> <void> +2597 # pseudocode: +2598 # var has-sib? = false, base = 0, index = 0, scale = 0 +2599 # var word-slice = {0, 0} +2600 # while true +2601 # word-slice = next-word(line) +2602 # if (slice-empty?(word-slice)) break +2603 # if (slice-starts-with?(word-slice, "#")) break +2604 # if (has-metadata?(word-slice, "base") +2605 # base = parse-hex-int(next-token-from-slice(word-slice, "/")) +2606 # has-sib? = true +2607 # else if (has-metadata?(word-slice, "index") +2608 # index = parse-hex-int(next-token-from-slice(word-slice, "/")) +2609 # has-sib? = true +2610 # else if (has-metadata?(word-slice, "scale") +2611 # scale = parse-hex-int(next-token-from-slice(word-slice, "/")) +2612 # has-sib? = true +2613 # if has-sib? +2614 # var sib = scale & 0b11 +2615 # sib <<= 2 +2616 # sib |= index & 0b111 +2617 # sib <<= 3 +2618 # sib |= base & 0b111 +2619 # emit-hex(out, sib, 1) +2620 # +2621 # . prolog +2622 55/push-EBP +2623 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2624 # . save registers +2625 50/push-EAX +2626 51/push-ECX +2627 52/push-EDX +2628 53/push-EBX +2629 56/push-ESI +2630 57/push-EDI +2631 # var word-slice/ECX = {0, 0} +2632 68/push 0/imm32/end +2633 68/push 0/imm32/start +2634 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2635 # var has-sib?/EDX = false +2636 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX +2637 # var scale/EBX = 0 +2638 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX +2639 # var base/ESI = 0 +2640 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI +2641 # var index/EDI = 0 +2642 31/xor 3/mod/direct 7/rm32/EDI . . . 7/r32/EDI . . # clear EDI +2643 # rewind-stream(line) +2644 # . . push args +2645 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2646 # . . call +2647 e8/call rewind-stream/disp32 +2648 # . . discard args +2649 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2650 $emit-sib:loop: +2651 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- +2677 # next-word(line, word-slice) +2678 # . . push args +2679 51/push-ECX +2680 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2681 # . . call +2682 e8/call next-word/disp32 +2683 # . . discard args +2684 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2685 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- +2727 $emit-sib:check0: +2728 # if (slice-empty?(word-slice)) break +2729 # . EAX = slice-empty?(word-slice) +2730 # . . push args +2731 51/push-ECX +2732 # . . call +2733 e8/call slice-empty?/disp32 +2734 # . . discard args +2735 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2736 # . if (EAX != 0) pass through +2737 3d/compare-EAX-and 0/imm32 +2738 0f 85/jump-if-not-equal $emit-sib:break/disp32 +2739 $emit-sib:check1: +2740 # if (slice-starts-with?(word-slice, "#")) break +2741 # . spill EDX +2742 52/push-EDX +2743 # . start/EDX = word-slice->start +2744 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX +2745 # . c/EAX = *start +2746 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2747 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL +2748 # . restore EDX +2749 5a/pop-to-EDX +2750 # . if (EAX == '#') pass through +2751 3d/compare-EAX-and 0x23/imm32/hash +2752 0f 84/jump-if-equal $emit-sib:break/disp32 +2753 $emit-sib:check-for-scale: +2754 # if (has-metadata?(word-slice, "scale")) +2755 # . EAX = has-metadata?(ECX, "scale") +2756 # . . push args +2757 68/push "scale"/imm32 +2758 51/push-ECX +2759 # . . call +2760 e8/call has-metadata?/disp32 +2761 # . . discard args +2762 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2763 # . if (EAX == 0) goto next check +2764 3d/compare-EAX-and 0/imm32 +2765 74/jump-if-equal $emit-sib:check-for-base/disp8 +2766 $emit-sib:scale: +2767 # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) +2768 # . EAX = parse-datum-of-word(word-slice) +2769 # . . push args +2770 51/push-ECX +2771 # . . call +2772 e8/call parse-datum-of-word/disp32 +2773 # . . discard args +2774 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2775 # . scale = EAX +2776 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX +2777 # has-sib? = true +2778 ba/copy-to-EDX 1/imm32/true +2779 # continue +2780 e9/jump $emit-sib:loop/disp32 +2781 $emit-sib:check-for-base: +2782 # if (has-metadata?(word-slice, "base")) +2783 # . EAX = has-metadata?(ECX, "base") +2784 # . . push args +2785 68/push "base"/imm32 +2786 51/push-ECX +2787 # . . call +2788 e8/call has-metadata?/disp32 +2789 # . . discard args +2790 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2791 # . if (EAX == 0) goto next check +2792 3d/compare-EAX-and 0/imm32 +2793 74/jump-if-equal $emit-sib:check-for-index/disp8 +2794 $emit-sib:base: +2795 # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) +2796 # . EAX = parse-datum-of-word(word-slice) +2797 # . . push args +2798 51/push-ECX +2799 # . . call +2800 e8/call parse-datum-of-word/disp32 +2801 # . . discard args +2802 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2803 # . base = EAX +2804 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI +2805 # has-sib? = true +2806 ba/copy-to-EDX 1/imm32/true +2807 # continue +2808 e9/jump $emit-sib:loop/disp32 +2809 $emit-sib:check-for-index: +2810 # if (has-metadata?(word-slice, "index")) +2811 # . EAX = has-metadata?(ECX, "index") +2812 # . . push args +2813 68/push "index"/imm32 +2814 51/push-ECX +2815 # . . call +2816 e8/call has-metadata?/disp32 +2817 # . . discard args +2818 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2819 # . if (EAX == 0) loop +2820 3d/compare-EAX-and 0/imm32 +2821 0f 84/jump-if-equal $emit-sib:loop/disp32 +2822 $emit-sib:index: +2823 # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) +2824 # . EAX = parse-datum-of-word(word-slice) +2825 # . . push args +2826 51/push-ECX +2827 # . . call +2828 e8/call parse-datum-of-word/disp32 +2829 # . . discard args +2830 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2831 # . index = EAX +2832 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI +2833 # has-sib? = true +2834 ba/copy-to-EDX 1/imm32/true +2835 # continue +2836 e9/jump $emit-sib:loop/disp32 +2837 $emit-sib:break: +2838 # if (!has-sib?) return +2839 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX +2840 74/jump-if-equal $emit-sib:end/disp8 +2841 $emit-sib:calculate: +2842 # sib/EBX = scale & 0b11 +2843 81 4/subop/and 3/mod/direct 3/rm32/EBX . . . . . 3/imm32/0b11 # bitwise and of EBX +2844 # sib <<= 2 +2845 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 2/imm8 # shift EBX left by 2 bits +2846 # sib |= index & 0b111 +2847 81 4/subop/and 3/mod/direct 7/rm32/EDI . . . . . 7/imm32/0b111 # bitwise and of EDI +2848 09/or 3/mod/direct 3/rm32/EBX . . . 7/r32/EDI . . # EBX = bitwise OR with EDI +2849 # sib <<= 3 +2850 c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 3/imm8 # shift EBX left by 3 bits +2851 # sib |= base & 0b111 +2852 81 4/subop/and 3/mod/direct 6/rm32/ESI . . . . . 7/imm32/0b111 # bitwise and of ESI +2853 09/or 3/mod/direct 3/rm32/EBX . . . 6/r32/ESI . . # EBX = bitwise OR with ESI +2854 $emit-sib:emit: +2855 # emit-hex(out, sib, 1) +2856 # . . push args +2857 68/push 1/imm32 +2858 53/push-EBX +2859 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +2860 # . . call +2861 e8/call emit-hex/disp32 +2862 # . . discard args +2863 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2864 $emit-sib:end: +2865 # . restore locals +2866 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2867 # . restore registers +2868 5f/pop-to-EDI +2869 5e/pop-to-ESI +2870 5b/pop-to-EBX +2871 5a/pop-to-EDX +2872 59/pop-to-ECX +2873 58/pop-to-EAX +2874 # . epilog +2875 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2876 5d/pop-to-EBP +2877 c3/return +2878 +2879 emit-disp: # line : (address stream byte), out : (address buffered-file) -> <void> +2880 # pseudocode: +2881 # rewind-stream(line) +2882 # var word-slice = {0, 0} +2883 # while true +2884 # word-slice = next-word(line) +2885 # if (slice-empty?(word-slice)) break +2886 # if (slice-starts-with?(word-slice, "#")) break +2887 # if has-metadata?(word-slice, "disp32") +2888 # emit(out, word-slice, 4) +2889 # break +2890 # if has-metadata?(word-slice, "disp16") +2891 # emit(out, word-slice, 2) +2892 # break +2893 # if has-metadata?(word-slice, "disp8") +2894 # emit(out, word-slice, 1) +2895 # break +2896 # +2897 # . prolog +2898 55/push-EBP +2899 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2900 # . save registers +2901 50/push-EAX +2902 51/push-ECX +2903 52/push-EDX +2904 # var word-slice/ECX = {0, 0} +2905 68/push 0/imm32/end +2906 68/push 0/imm32/start +2907 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2908 # rewind-stream(line) +2909 # . . push args +2910 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2911 # . . call +2912 e8/call rewind-stream/disp32 +2913 # . . discard args +2914 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2915 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- +2941 $emit-disp:loop: +2942 # next-word(line, word-slice) +2943 # . . push args +2944 51/push-ECX +2945 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +2946 # . . call +2947 e8/call next-word/disp32 +2948 # . . discard args +2949 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2950 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- +2992 $emit-disp:check0: +2993 # if (slice-empty?(word-slice)) break +2994 # . EAX = slice-empty?(word-slice) +2995 # . . push args +2996 51/push-ECX +2997 # . . call +2998 e8/call slice-empty?/disp32 +2999 # . . discard args +3000 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3001 # . if (EAX != 0) pass through +3002 3d/compare-EAX-and 0/imm32 +3003 0f 85/jump-if-not-equal $emit-disp:break/disp32 +3004 $emit-disp:check1: +3005 # if (slice-starts-with?(word-slice, "#")) break +3006 # . start/EDX = word-slice->start +3007 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX +3008 # . c/EAX = *start +3009 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +3010 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL +3011 # . if (EAX == '#') break +3012 3d/compare-EAX-and 0x23/imm32/hash +3013 0f 84/jump-if-equal $emit-disp:break/disp32 +3014 $emit-disp:check-for-disp32: +3015 # if (has-metadata?(word-slice, "disp32")) +3016 # . EAX = has-metadata?(ECX, "disp32") +3017 # . . push args +3018 68/push "disp32"/imm32 +3019 51/push-ECX +3020 # . . call +3021 e8/call has-metadata?/disp32 +3022 # . . discard args +3023 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3024 # . if (EAX == 0) goto next check +3025 3d/compare-EAX-and 0/imm32 +3026 74/jump-if-equal $emit-disp:check-for-disp16/disp8 +3027 $emit-disp:disp32: +3028 # emit(out, word-slice, 4) +3029 # . . push args +3030 68/push 4/imm32 +3031 51/push-ECX +3032 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3033 # . . call +3034 e8/call emit/disp32 +3035 # . . discard args +3036 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3037 # break +3038 e9/jump $emit-disp:break/disp32 +3039 $emit-disp:check-for-disp16: +3040 # else if (has-metadata?(word-slice, "disp16")) +3041 # . EAX = has-metadata?(ECX, "disp16") +3042 # . . push args +3043 68/push "disp16"/imm32 +3044 51/push-ECX +3045 # . . call +3046 e8/call has-metadata?/disp32 +3047 # . . discard args +3048 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3049 # . if (EAX == 0) goto next check +3050 3d/compare-EAX-and 0/imm32 +3051 74/jump-if-equal $emit-disp:check-for-disp8/disp8 +3052 $emit-disp:disp16: +3053 # emit(out, word-slice, 2) +3054 # . . push args +3055 68/push 2/imm32 +3056 51/push-ECX +3057 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3058 # . . call +3059 e8/call emit/disp32 +3060 # . . discard args +3061 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3062 # break +3063 e9/jump $emit-disp:break/disp32 +3064 $emit-disp:check-for-disp8: +3065 # if (has-metadata?(word-slice, "disp8")) +3066 # . EAX = has-metadata?(ECX, "disp8") +3067 # . . push args +3068 68/push "disp8"/imm32 +3069 51/push-ECX +3070 # . . call +3071 e8/call has-metadata?/disp32 +3072 # . . discard args +3073 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3074 # . if (EAX == 0) loop +3075 3d/compare-EAX-and 0/imm32 +3076 0f 84/jump-if-equal $emit-disp:loop/disp32 +3077 $emit-disp:disp8: +3078 # emit(out, word-slice, 1) +3079 # . . push args +3080 68/push 1/imm32 +3081 51/push-ECX +3082 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3083 # . . call +3084 e8/call emit/disp32 +3085 # . . discard args +3086 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3087 # break +3088 $emit-disp:break: +3089 # . restore locals +3090 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3091 # . restore registers +3092 5a/pop-to-EDX +3093 59/pop-to-ECX +3094 58/pop-to-EAX +3095 # . epilog +3096 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3097 5d/pop-to-EBP +3098 c3/return +3099 +3100 emit-imm: # line : (address stream byte), out : (address buffered-file) -> <void> +3101 # pseudocode: +3102 # rewind-stream(line) +3103 # var word-slice = {0, 0} +3104 # while true +3105 # word-slice = next-word(line) +3106 # if (slice-empty?(word-slice)) break +3107 # if (slice-starts-with?(word-slice, "#")) break +3108 # if has-metadata?(word-slice, "imm32") +3109 # emit(out, word-slice, 4) +3110 # break +3111 # if has-metadata?(word-slice, "imm16") +3112 # emit(out, word-slice, 2) +3113 # break +3114 # if has-metadata?(word-slice, "imm8") +3115 # emit(out, word-slice, 1) +3116 # break +3117 # +3118 # . prolog +3119 55/push-EBP +3120 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3121 # . save registers +3122 50/push-EAX +3123 51/push-ECX +3124 52/push-EDX +3125 # var word-slice/ECX = {0, 0} +3126 68/push 0/imm32/end +3127 68/push 0/imm32/start +3128 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +3129 # rewind-stream(line) +3130 # . . push args +3131 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +3132 # . . call +3133 e8/call rewind-stream/disp32 +3134 # . . discard args +3135 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3136 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- +3162 $emit-imm:loop: +3163 # next-word(line, word-slice) +3164 # . . push args +3165 51/push-ECX +3166 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +3167 # . . call +3168 e8/call next-word/disp32 +3169 # . . discard args +3170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3171 +-- 42 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- +3213 $emit-imm:check0: +3214 # if (slice-empty?(word-slice)) break +3215 # . EAX = slice-empty?(word-slice) +3216 # . . push args +3217 51/push-ECX +3218 # . . call +3219 e8/call slice-empty?/disp32 +3220 # . . discard args +3221 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3222 # . if (EAX != 0) pass through +3223 3d/compare-EAX-and 0/imm32 +3224 0f 85/jump-if-not-equal $emit-imm:break/disp32 +3225 $emit-imm:check1: +3226 # if (slice-starts-with?(word-slice, "#")) break +3227 # . start/EDX = slice->start +3228 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX +3229 # . c/EAX = *start +3230 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +3231 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL +3232 # . if (EAX == '#') break +3233 3d/compare-EAX-and 0x23/imm32/hash +3234 0f 84/jump-if-equal $emit-imm:break/disp32 +3235 $emit-imm:check-for-imm32: +3236 # if (has-metadata?(word-slice, "imm32")) +3237 # . EAX = has-metadata?(ECX, "imm32") +3238 # . . push args +3239 68/push "imm32"/imm32 +3240 51/push-ECX +3241 # . . call +3242 e8/call has-metadata?/disp32 +3243 # . . discard args +3244 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3245 # . if (EAX == 0) goto next check +3246 3d/compare-EAX-and 0/imm32 +3247 74/jump-if-equal $emit-imm:check-for-imm16/disp8 +3248 $emit-imm:imm32: +3249 # emit(out, word-slice, 4) +3250 # . . push args +3251 68/push 4/imm32 +3252 51/push-ECX +3253 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3254 # . . call +3255 e8/call emit/disp32 +3256 # . . discard args +3257 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3258 # break +3259 e9/jump $emit-imm:break/disp32 +3260 $emit-imm:check-for-imm16: +3261 # if (has-metadata?(word-slice, "imm16")) +3262 # . EAX = has-metadata?(ECX, "imm16") +3263 # . . push args +3264 68/push "imm16"/imm32 +3265 51/push-ECX +3266 # . . call +3267 e8/call has-metadata?/disp32 +3268 # . . discard args +3269 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3270 # . if (EAX == 0) goto next check +3271 3d/compare-EAX-and 0/imm32 +3272 74/jump-if-equal $emit-imm:check-for-imm8/disp8 +3273 $emit-imm:imm16: +3274 # emit(out, word-slice, 2) +3275 # . . push args +3276 68/push 2/imm32 +3277 51/push-ECX +3278 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3279 # . . call +3280 e8/call emit/disp32 +3281 # . . discard args +3282 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3283 # break +3284 e9/jump $emit-imm:break/disp32 +3285 $emit-imm:check-for-imm8: +3286 # if (has-metadata?(word-slice, "imm8")) +3287 # . EAX = has-metadata?(ECX, "imm8") +3288 # . . push args +3289 68/push "imm8"/imm32 +3290 51/push-ECX +3291 # . . call +3292 e8/call has-metadata?/disp32 +3293 # . . discard args +3294 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3295 # . if (EAX == 0) loop +3296 3d/compare-EAX-and 0/imm32 +3297 0f 84/jump-if-equal $emit-imm:loop/disp32 +3298 $emit-imm:imm8: +3299 # emit(out, word-slice, 1) +3300 # . . push args +3301 68/push 1/imm32 +3302 51/push-ECX +3303 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3304 # . . call +3305 e8/call emit/disp32 +3306 # . . discard args +3307 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3308 # break +3309 $emit-imm:break: +3310 # . restore locals +3311 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3312 # . restore registers +3313 5a/pop-to-EDX +3314 59/pop-to-ECX +3315 58/pop-to-EAX +3316 # . epilog +3317 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3318 5d/pop-to-EBP +3319 c3/return +3320 +3321 emit-line-in-comment: # line : (address stream byte), out : (address buffered-file) -> <void> +3322 # . prolog +3323 55/push-EBP +3324 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3325 # write-buffered(out, " # ") +3326 # . . push args +3327 68/push " # "/imm32 +3328 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3329 # . . call +3330 e8/call write-buffered/disp32 +3331 # . . discard args +3332 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3333 # write-stream-data(out, line) +3334 # . . push args +3335 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +3336 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +3337 # . . call +3338 e8/call write-stream-data/disp32 +3339 # . . discard args +3340 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3341 $emit-line-in-comment:end: +3342 # . epilog +3343 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3344 5d/pop-to-EBP +3345 c3/return +3346 +3347 test-convert-instruction-passes-comments-through: +3348 # if a line starts with '#', pass it along unchanged +3349 # . prolog +3350 55/push-EBP +3351 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3352 # setup +3353 # . clear-stream(_test-input-stream) +3354 # . . push args +3355 68/push _test-input-stream/imm32 +3356 # . . call +3357 e8/call clear-stream/disp32 +3358 # . . discard args +3359 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3360 # . clear-stream(_test-output-stream) +3361 # . . push args +3362 68/push _test-output-stream/imm32 +3363 # . . call +3364 e8/call clear-stream/disp32 +3365 # . . discard args +3366 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3367 # . clear-stream(_test-output-buffered-file+4) +3368 # . . push args +3369 b8/copy-to-EAX _test-output-buffered-file/imm32 +3370 05/add-to-EAX 4/imm32 +3371 50/push-EAX +3372 # . . call +3373 e8/call clear-stream/disp32 +3374 # . . discard args +3375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3376 # initialize input +3377 # . write(_test-input-stream, "# abcd") +3378 # . . push args +3379 68/push "# abcd"/imm32 +3380 68/push _test-input-stream/imm32 +3381 # . . call +3382 e8/call write/disp32 +3383 # . . discard args +3384 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3385 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3386 # . . push args +3387 68/push _test-output-buffered-file/imm32 +3388 68/push _test-input-stream/imm32 +3389 # . . call +3390 e8/call convert-instruction/disp32 +3391 # . . discard args +3392 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3393 # check that the line just passed through +3394 # . flush(_test-output-buffered-file) +3395 # . . push args +3396 68/push _test-output-buffered-file/imm32 +3397 # . . call +3398 e8/call flush/disp32 +3399 # . . discard args +3400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3401 # . check-stream-equal(_test-output-stream, "# abcd", msg) +3402 # . . push args +3403 68/push "F - test-convert-instruction-passes-comments-through"/imm32 +3404 68/push "# abcd"/imm32 +3405 68/push _test-output-stream/imm32 +3406 # . . call +3407 e8/call check-stream-equal/disp32 +3408 # . . discard args +3409 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3410 # . epilog +3411 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3412 5d/pop-to-EBP +3413 c3/return +3414 +3415 test-convert-instruction-passes-labels-through: +3416 # if the first word ends with ':', pass along the entire line unchanged +3417 # . prolog +3418 55/push-EBP +3419 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3420 # setup +3421 # . clear-stream(_test-input-stream) +3422 # . . push args +3423 68/push _test-input-stream/imm32 +3424 # . . call +3425 e8/call clear-stream/disp32 +3426 # . . discard args +3427 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3428 # . clear-stream(_test-output-stream) +3429 # . . push args +3430 68/push _test-output-stream/imm32 +3431 # . . call +3432 e8/call clear-stream/disp32 +3433 # . . discard args +3434 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3435 # . clear-stream(_test-output-buffered-file+4) +3436 # . . push args +3437 b8/copy-to-EAX _test-output-buffered-file/imm32 +3438 05/add-to-EAX 4/imm32 +3439 50/push-EAX +3440 # . . call +3441 e8/call clear-stream/disp32 +3442 # . . discard args +3443 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3444 # initialize input +3445 # . write(_test-input-stream, "ab: # cd") +3446 # . . push args +3447 68/push "ab: # cd"/imm32 +3448 68/push _test-input-stream/imm32 +3449 # . . call +3450 e8/call write/disp32 +3451 # . . discard args +3452 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3453 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3454 # . . push args +3455 68/push _test-output-buffered-file/imm32 +3456 68/push _test-input-stream/imm32 +3457 # . . call +3458 e8/call convert-instruction/disp32 +3459 # . . discard args +3460 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3461 # check that the line just passed through +3462 # . flush(_test-output-buffered-file) +3463 # . . push args +3464 68/push _test-output-buffered-file/imm32 +3465 # . . call +3466 e8/call flush/disp32 +3467 # . . discard args +3468 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3469 # . check-stream-equal(_test-output-stream, "ab: # cd", msg) +3470 # . . push args +3471 68/push "F - test-convert-instruction-passes-labels-through"/imm32 +3472 68/push "ab: # cd"/imm32 +3473 68/push _test-output-stream/imm32 +3474 # . . call +3475 e8/call check-stream-equal/disp32 +3476 # . . discard args +3477 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3478 # . epilog +3479 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3480 5d/pop-to-EBP +3481 c3/return +3482 +3483 test-convert-instruction-handles-single-opcode: +3484 # if the instruction consists of a single opcode, strip its metadata and pass it along +3485 # . prolog +3486 55/push-EBP +3487 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3488 # setup +3489 # . clear-stream(_test-input-stream) +3490 # . . push args +3491 68/push _test-input-stream/imm32 +3492 # . . call +3493 e8/call clear-stream/disp32 +3494 # . . discard args +3495 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3496 # . clear-stream(_test-output-stream) +3497 # . . push args +3498 68/push _test-output-stream/imm32 +3499 # . . call +3500 e8/call clear-stream/disp32 +3501 # . . discard args +3502 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3503 # . clear-stream(_test-output-buffered-file+4) +3504 # . . push args +3505 b8/copy-to-EAX _test-output-buffered-file/imm32 +3506 05/add-to-EAX 4/imm32 +3507 50/push-EAX +3508 # . . call +3509 e8/call clear-stream/disp32 +3510 # . . discard args +3511 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3512 # initialize input +3513 # . write(_test-input-stream, "ab/cd # comment") +3514 # . . push args +3515 68/push "ab/cd # comment"/imm32 +3516 68/push _test-input-stream/imm32 +3517 # . . call +3518 e8/call write/disp32 +3519 # . . discard args +3520 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3521 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3522 # . . push args +3523 68/push _test-output-buffered-file/imm32 +3524 68/push _test-input-stream/imm32 +3525 # . . call +3526 e8/call convert-instruction/disp32 +3527 # . . discard args +3528 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3529 # check output +3530 # . flush(_test-output-buffered-file) +3531 # . . push args +3532 68/push _test-output-buffered-file/imm32 +3533 # . . call +3534 e8/call flush/disp32 +3535 # . . discard args +3536 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3537 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +3563 # . check-stream-equal(_test-output-stream, "ab # ab/cd # comment", msg) +3564 # . . push args +3565 68/push "F - test-convert-instruction-handles-single-opcode"/imm32 +3566 68/push "ab # ab/cd # comment"/imm32 +3567 68/push _test-output-stream/imm32 +3568 # . . call +3569 e8/call check-stream-equal/disp32 +3570 # . . discard args +3571 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3572 # . epilog +3573 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3574 5d/pop-to-EBP +3575 c3/return +3576 +3577 test-convert-instruction-handles-0f-opcode: +3578 # if the instruction starts with 0f opcode, include a second opcode +3579 # . prolog +3580 55/push-EBP +3581 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3582 # setup +3583 # . clear-stream(_test-input-stream) +3584 # . . push args +3585 68/push _test-input-stream/imm32 +3586 # . . call +3587 e8/call clear-stream/disp32 +3588 # . . discard args +3589 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3590 # . clear-stream(_test-output-stream) +3591 # . . push args +3592 68/push _test-output-stream/imm32 +3593 # . . call +3594 e8/call clear-stream/disp32 +3595 # . . discard args +3596 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3597 # . clear-stream(_test-output-buffered-file+4) +3598 # . . push args +3599 b8/copy-to-EAX _test-output-buffered-file/imm32 +3600 05/add-to-EAX 4/imm32 +3601 50/push-EAX +3602 # . . call +3603 e8/call clear-stream/disp32 +3604 # . . discard args +3605 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3606 # initialize input +3607 # . write(_test-input-stream, "0f/m1 ab/m2 # comment") +3608 # . . push args +3609 68/push "0f/m1 ab/m2 # comment"/imm32 +3610 68/push _test-input-stream/imm32 +3611 # . . call +3612 e8/call write/disp32 +3613 # . . discard args +3614 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3615 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3616 # . . push args +3617 68/push _test-output-buffered-file/imm32 +3618 68/push _test-input-stream/imm32 +3619 # . . call +3620 e8/call convert-instruction/disp32 +3621 # . . discard args +3622 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3623 # check output +3624 # . flush(_test-output-buffered-file) +3625 # . . push args +3626 68/push _test-output-buffered-file/imm32 +3627 # . . call +3628 e8/call flush/disp32 +3629 # . . discard args +3630 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3631 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +3657 # . check-stream-equal(_test-output-stream, "0f ab # 0f/m1 ab/m2 # comment", msg) +3658 # . . push args +3659 68/push "F - test-convert-instruction-handles-0f-opcode"/imm32 +3660 68/push "0f ab # 0f/m1 ab/m2 # comment"/imm32 +3661 68/push _test-output-stream/imm32 +3662 # . . call +3663 e8/call check-stream-equal/disp32 +3664 # . . discard args +3665 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3666 # . epilog +3667 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3668 5d/pop-to-EBP +3669 c3/return +3670 +3671 test-convert-instruction-handles-f2-opcode: +3672 # if the instruction starts with f2 opcode, include a second opcode +3673 # . prolog +3674 55/push-EBP +3675 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3676 # setup +3677 # . clear-stream(_test-input-stream) +3678 # . . push args +3679 68/push _test-input-stream/imm32 +3680 # . . call +3681 e8/call clear-stream/disp32 +3682 # . . discard args +3683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3684 # . clear-stream(_test-output-stream) +3685 # . . push args +3686 68/push _test-output-stream/imm32 +3687 # . . call +3688 e8/call clear-stream/disp32 +3689 # . . discard args +3690 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3691 # . clear-stream(_test-output-buffered-file+4) +3692 # . . push args +3693 b8/copy-to-EAX _test-output-buffered-file/imm32 +3694 05/add-to-EAX 4/imm32 +3695 50/push-EAX +3696 # . . call +3697 e8/call clear-stream/disp32 +3698 # . . discard args +3699 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3700 # initialize input +3701 # . write(_test-input-stream, "f2/m1 ab/m2 # comment") +3702 # . . push args +3703 68/push "f2/m1 ab/m2 # comment"/imm32 +3704 68/push _test-input-stream/imm32 +3705 # . . call +3706 e8/call write/disp32 +3707 # . . discard args +3708 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3709 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3710 # . . push args +3711 68/push _test-output-buffered-file/imm32 +3712 68/push _test-input-stream/imm32 +3713 # . . call +3714 e8/call convert-instruction/disp32 +3715 # . . discard args +3716 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3717 # check output +3718 # . flush(_test-output-buffered-file) +3719 # . . push args +3720 68/push _test-output-buffered-file/imm32 +3721 # . . call +3722 e8/call flush/disp32 +3723 # . . discard args +3724 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3725 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +3751 # . check-stream-equal(_test-output-stream, "f2 ab # f2/m1 ab/m2 # comment", msg) +3752 # . . push args +3753 68/push "F - test-convert-instruction-handles-f2-opcode"/imm32 +3754 68/push "f2 ab # f2/m1 ab/m2 # comment"/imm32 +3755 68/push _test-output-stream/imm32 +3756 # . . call +3757 e8/call check-stream-equal/disp32 +3758 # . . discard args +3759 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3760 # . epilog +3761 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3762 5d/pop-to-EBP +3763 c3/return +3764 +3765 test-convert-instruction-handles-f3-opcode: +3766 # if the instruction starts with f3 opcode, include a second opcode +3767 # . prolog +3768 55/push-EBP +3769 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3770 # setup +3771 # . clear-stream(_test-input-stream) +3772 # . . push args +3773 68/push _test-input-stream/imm32 +3774 # . . call +3775 e8/call clear-stream/disp32 +3776 # . . discard args +3777 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3778 # . clear-stream(_test-output-stream) +3779 # . . push args +3780 68/push _test-output-stream/imm32 +3781 # . . call +3782 e8/call clear-stream/disp32 +3783 # . . discard args +3784 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3785 # . clear-stream(_test-output-buffered-file+4) +3786 # . . push args +3787 b8/copy-to-EAX _test-output-buffered-file/imm32 +3788 05/add-to-EAX 4/imm32 +3789 50/push-EAX +3790 # . . call +3791 e8/call clear-stream/disp32 +3792 # . . discard args +3793 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3794 # initialize input +3795 # . write(_test-input-stream, "f3/m1 ab/m2 # comment") +3796 # . . push args +3797 68/push "f3/m1 ab/m2 # comment"/imm32 +3798 68/push _test-input-stream/imm32 +3799 # . . call +3800 e8/call write/disp32 +3801 # . . discard args +3802 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3803 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3804 # . . push args +3805 68/push _test-output-buffered-file/imm32 +3806 68/push _test-input-stream/imm32 +3807 # . . call +3808 e8/call convert-instruction/disp32 +3809 # . . discard args +3810 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3811 # check output +3812 # . flush(_test-output-buffered-file) +3813 # . . push args +3814 68/push _test-output-buffered-file/imm32 +3815 # . . call +3816 e8/call flush/disp32 +3817 # . . discard args +3818 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3819 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +3845 # . check-stream-equal(_test-output-stream, "f3 ab # f3/m1 ab/m2 # comment", msg) +3846 # . . push args +3847 68/push "F - test-convert-instruction-handles-f3-opcode"/imm32 +3848 68/push "f3 ab # f3/m1 ab/m2 # comment"/imm32 +3849 68/push _test-output-stream/imm32 +3850 # . . call +3851 e8/call check-stream-equal/disp32 +3852 # . . discard args +3853 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3854 # . epilog +3855 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3856 5d/pop-to-EBP +3857 c3/return +3858 +3859 test-convert-instruction-handles-f2-0f-opcode: +3860 # if the instruction starts with f2 0f opcode, include a second opcode +3861 # . prolog +3862 55/push-EBP +3863 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3864 # setup +3865 # . clear-stream(_test-input-stream) +3866 # . . push args +3867 68/push _test-input-stream/imm32 +3868 # . . call +3869 e8/call clear-stream/disp32 +3870 # . . discard args +3871 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3872 # . clear-stream(_test-output-stream) +3873 # . . push args +3874 68/push _test-output-stream/imm32 +3875 # . . call +3876 e8/call clear-stream/disp32 +3877 # . . discard args +3878 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3879 # . clear-stream(_test-output-buffered-file+4) +3880 # . . push args +3881 b8/copy-to-EAX _test-output-buffered-file/imm32 +3882 05/add-to-EAX 4/imm32 +3883 50/push-EAX +3884 # . . call +3885 e8/call clear-stream/disp32 +3886 # . . discard args +3887 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3888 # initialize input +3889 # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment") +3890 # . . push args +3891 68/push "f2/m1 0f/m2 ab/m3 # comment"/imm32 +3892 68/push _test-input-stream/imm32 +3893 # . . call +3894 e8/call write/disp32 +3895 # . . discard args +3896 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3897 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3898 # . . push args +3899 68/push _test-output-buffered-file/imm32 +3900 68/push _test-input-stream/imm32 +3901 # . . call +3902 e8/call convert-instruction/disp32 +3903 # . . discard args +3904 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3905 # check output +3906 # . flush(_test-output-buffered-file) +3907 # . . push args +3908 68/push _test-output-buffered-file/imm32 +3909 # . . call +3910 e8/call flush/disp32 +3911 # . . discard args +3912 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3913 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +3939 # . check-stream-equal(_test-output-stream, "f2 0f ab # f2/m1 0f/m2 ab/m3 # comment", msg) +3940 # . . push args +3941 68/push "F - test-convert-instruction-handles-f2-0f-opcode"/imm32 +3942 68/push "f2 0f ab # f2/m1 0f/m2 ab/m3 # comment"/imm32 +3943 68/push _test-output-stream/imm32 +3944 # . . call +3945 e8/call check-stream-equal/disp32 +3946 # . . discard args +3947 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +3948 # . epilog +3949 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +3950 5d/pop-to-EBP +3951 c3/return +3952 +3953 test-convert-instruction-handles-f3-0f-opcode: +3954 # if the instruction starts with f3 0f opcode, include a second opcode +3955 # . prolog +3956 55/push-EBP +3957 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +3958 # setup +3959 # . clear-stream(_test-input-stream) +3960 # . . push args +3961 68/push _test-input-stream/imm32 +3962 # . . call +3963 e8/call clear-stream/disp32 +3964 # . . discard args +3965 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3966 # . clear-stream(_test-output-stream) +3967 # . . push args +3968 68/push _test-output-stream/imm32 +3969 # . . call +3970 e8/call clear-stream/disp32 +3971 # . . discard args +3972 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3973 # . clear-stream(_test-output-buffered-file+4) +3974 # . . push args +3975 b8/copy-to-EAX _test-output-buffered-file/imm32 +3976 05/add-to-EAX 4/imm32 +3977 50/push-EAX +3978 # . . call +3979 e8/call clear-stream/disp32 +3980 # . . discard args +3981 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +3982 # initialize input +3983 # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment") +3984 # . . push args +3985 68/push "f3/m1 0f/m2 ab/m3 # comment"/imm32 +3986 68/push _test-input-stream/imm32 +3987 # . . call +3988 e8/call write/disp32 +3989 # . . discard args +3990 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3991 # convert-instruction(_test-input-stream, _test-output-buffered-file) +3992 # . . push args +3993 68/push _test-output-buffered-file/imm32 +3994 68/push _test-input-stream/imm32 +3995 # . . call +3996 e8/call convert-instruction/disp32 +3997 # . . discard args +3998 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +3999 # check output +4000 # . flush(_test-output-buffered-file) +4001 # . . push args +4002 68/push _test-output-buffered-file/imm32 +4003 # . . call +4004 e8/call flush/disp32 +4005 # . . discard args +4006 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4007 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4033 # . check-stream-equal(_test-output-stream, "f3 0f ab # f3/m1 0f/m2 ab/m3 # comment", msg) +4034 # . . push args +4035 68/push "F - test-convert-instruction-handles-f3-0f-opcode"/imm32 +4036 68/push "f3 0f ab # f3/m1 0f/m2 ab/m3 # comment"/imm32 +4037 68/push _test-output-stream/imm32 +4038 # . . call +4039 e8/call check-stream-equal/disp32 +4040 # . . discard args +4041 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4042 # . epilog +4043 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4044 5d/pop-to-EBP +4045 c3/return +4046 +4047 test-convert-instruction-handles-unused-opcodes: +4048 # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes +4049 # . prolog +4050 55/push-EBP +4051 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4052 # setup +4053 # . clear-stream(_test-input-stream) +4054 # . . push args +4055 68/push _test-input-stream/imm32 +4056 # . . call +4057 e8/call clear-stream/disp32 +4058 # . . discard args +4059 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4060 # . clear-stream(_test-output-stream) +4061 # . . push args +4062 68/push _test-output-stream/imm32 +4063 # . . call +4064 e8/call clear-stream/disp32 +4065 # . . discard args +4066 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4067 # . clear-stream(_test-output-buffered-file+4) +4068 # . . push args +4069 b8/copy-to-EAX _test-output-buffered-file/imm32 +4070 05/add-to-EAX 4/imm32 +4071 50/push-EAX +4072 # . . call +4073 e8/call clear-stream/disp32 +4074 # . . discard args +4075 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4076 # initialize input +4077 # . write(_test-input-stream, "ab/m1 cd/m2 # comment") +4078 # . . push args +4079 68/push "ab/m1 cd/m2 # comment"/imm32 +4080 68/push _test-input-stream/imm32 +4081 # . . call +4082 e8/call write/disp32 +4083 # . . discard args +4084 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4085 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4086 # . . push args +4087 68/push _test-output-buffered-file/imm32 +4088 68/push _test-input-stream/imm32 +4089 # . . call +4090 e8/call convert-instruction/disp32 +4091 # . . discard args +4092 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4093 # check output +4094 # . flush(_test-output-buffered-file) +4095 # . . push args +4096 68/push _test-output-buffered-file/imm32 +4097 # . . call +4098 e8/call flush/disp32 +4099 # . . discard args +4100 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4101 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4127 # . check-stream-equal(_test-output-stream, "ab # f3/m1 0f/m2 ab/m3 # comment", msg) +4128 # . . push args +4129 68/push "F - test-convert-instruction-handles-unused-opcodes"/imm32 +4130 68/push "ab # ab/m1 cd/m2 # comment"/imm32 +4131 68/push _test-output-stream/imm32 +4132 # . . call +4133 e8/call check-stream-equal/disp32 +4134 # . . discard args +4135 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4136 # . epilog +4137 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4138 5d/pop-to-EBP +4139 c3/return +4140 +4141 test-convert-instruction-handles-unused-second-opcodes: +4142 # if the second opcode isn't 0f, don't include further opcodes +4143 # . prolog +4144 55/push-EBP +4145 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4146 # setup +4147 # . clear-stream(_test-input-stream) +4148 # . . push args +4149 68/push _test-input-stream/imm32 +4150 # . . call +4151 e8/call clear-stream/disp32 +4152 # . . discard args +4153 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4154 # . clear-stream(_test-output-stream) +4155 # . . push args +4156 68/push _test-output-stream/imm32 +4157 # . . call +4158 e8/call clear-stream/disp32 +4159 # . . discard args +4160 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4161 # . clear-stream(_test-output-buffered-file+4) +4162 # . . push args +4163 b8/copy-to-EAX _test-output-buffered-file/imm32 +4164 05/add-to-EAX 4/imm32 +4165 50/push-EAX +4166 # . . call +4167 e8/call clear-stream/disp32 +4168 # . . discard args +4169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4170 # initialize input +4171 # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment") +4172 # . . push args +4173 68/push "f2/m1 ab/m2 cd/m3 # comment"/imm32 +4174 68/push _test-input-stream/imm32 +4175 # . . call +4176 e8/call write/disp32 +4177 # . . discard args +4178 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4179 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4180 # . . push args +4181 68/push _test-output-buffered-file/imm32 +4182 68/push _test-input-stream/imm32 +4183 # . . call +4184 e8/call convert-instruction/disp32 +4185 # . . discard args +4186 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4187 # check output +4188 # . flush(_test-output-buffered-file) +4189 # . . push args +4190 68/push _test-output-buffered-file/imm32 +4191 # . . call +4192 e8/call flush/disp32 +4193 # . . discard args +4194 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4195 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4221 # . check-stream-equal(_test-output-stream, "f2 ab # f2/m1 ab/m2 cd/m3 # comment", msg) +4222 # . . push args +4223 68/push "F - test-convert-instruction-handles-unused-second-opcodes"/imm32 +4224 68/push "f2 ab # f2/m1 ab/m2 cd/m3 # comment"/imm32 +4225 68/push _test-output-stream/imm32 +4226 # . . call +4227 e8/call check-stream-equal/disp32 +4228 # . . discard args +4229 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4230 # . epilog +4231 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4232 5d/pop-to-EBP +4233 c3/return +4234 +4235 test-convert-instruction-handles-unused-second-opcodes-2: +4236 # if the second opcode isn't 0f, don't include further opcodes +4237 # . prolog +4238 55/push-EBP +4239 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4240 # setup +4241 # . clear-stream(_test-input-stream) +4242 # . . push args +4243 68/push _test-input-stream/imm32 +4244 # . . call +4245 e8/call clear-stream/disp32 +4246 # . . discard args +4247 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4248 # . clear-stream(_test-output-stream) +4249 # . . push args +4250 68/push _test-output-stream/imm32 +4251 # . . call +4252 e8/call clear-stream/disp32 +4253 # . . discard args +4254 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4255 # . clear-stream(_test-output-buffered-file+4) +4256 # . . push args +4257 b8/copy-to-EAX _test-output-buffered-file/imm32 +4258 05/add-to-EAX 4/imm32 +4259 50/push-EAX +4260 # . . call +4261 e8/call clear-stream/disp32 +4262 # . . discard args +4263 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4264 # initialize input +4265 # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment") +4266 # . . push args +4267 68/push "f3/m1 ab/m2 cd/m3 # comment"/imm32 +4268 68/push _test-input-stream/imm32 +4269 # . . call +4270 e8/call write/disp32 +4271 # . . discard args +4272 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4273 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4274 # . . push args +4275 68/push _test-output-buffered-file/imm32 +4276 68/push _test-input-stream/imm32 +4277 # . . call +4278 e8/call convert-instruction/disp32 +4279 # . . discard args +4280 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4281 # check output +4282 # . flush(_test-output-buffered-file) +4283 # . . push args +4284 68/push _test-output-buffered-file/imm32 +4285 # . . call +4286 e8/call flush/disp32 +4287 # . . discard args +4288 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4289 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4315 # . check-stream-equal(_test-output-stream, "f3 ab # f3/m1 ab/m2 cd/m3 # comment", msg) +4316 # . . push args +4317 68/push "F - test-convert-instruction-handles-unused-second-opcodes"/imm32 +4318 68/push "f3 ab # f3/m1 ab/m2 cd/m3 # comment"/imm32 +4319 68/push _test-output-stream/imm32 +4320 # . . call +4321 e8/call check-stream-equal/disp32 +4322 # . . discard args +4323 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4324 # . epilog +4325 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4326 5d/pop-to-EBP +4327 c3/return +4328 +4329 test-convert-instruction-emits-modrm-byte: +4330 # pack mod, rm32 and r32 operands into ModR/M byte +4331 # . prolog +4332 55/push-EBP +4333 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4334 # setup +4335 # . clear-stream(_test-input-stream) +4336 # . . push args +4337 68/push _test-input-stream/imm32 +4338 # . . call +4339 e8/call clear-stream/disp32 +4340 # . . discard args +4341 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4342 # . clear-stream(_test-output-stream) +4343 # . . push args +4344 68/push _test-output-stream/imm32 +4345 # . . call +4346 e8/call clear-stream/disp32 +4347 # . . discard args +4348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4349 # . clear-stream(_test-output-buffered-file+4) +4350 # . . push args +4351 b8/copy-to-EAX _test-output-buffered-file/imm32 +4352 05/add-to-EAX 4/imm32 +4353 50/push-EAX +4354 # . . call +4355 e8/call clear-stream/disp32 +4356 # . . discard args +4357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4358 # initialize input +4359 # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32") +4360 # . . push args +4361 68/push "8b/copy 0/mod 0/rm32 1/r32"/imm32 +4362 68/push _test-input-stream/imm32 +4363 # . . call +4364 e8/call write/disp32 +4365 # . . discard args +4366 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4367 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4368 # . . push args +4369 68/push _test-output-buffered-file/imm32 +4370 68/push _test-input-stream/imm32 +4371 # . . call +4372 e8/call convert-instruction/disp32 +4373 # . . discard args +4374 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4375 # check output +4376 # . flush(_test-output-buffered-file) +4377 # . . push args +4378 68/push _test-output-buffered-file/imm32 +4379 # . . call +4380 e8/call flush/disp32 +4381 # . . discard args +4382 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4383 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4409 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 0/rm32 1/r32", msg) +4410 # . . push args +4411 68/push "F - test-convert-instruction-emits-modrm-byte"/imm32 +4412 68/push "8b 08 # 8b/copy 0/mod 0/rm32 1/r32"/imm32 +4413 68/push _test-output-stream/imm32 +4414 # . . call +4415 e8/call check-stream-equal/disp32 +4416 # . . discard args +4417 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4418 # . epilog +4419 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4420 5d/pop-to-EBP +4421 c3/return +4422 +4423 test-convert-instruction-emits-modrm-byte-from-subop: +4424 # pack mod, rm32 and subop operands into ModR/M byte +4425 # . prolog +4426 55/push-EBP +4427 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4428 # setup +4429 # . clear-stream(_test-input-stream) +4430 # . . push args +4431 68/push _test-input-stream/imm32 +4432 # . . call +4433 e8/call clear-stream/disp32 +4434 # . . discard args +4435 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4436 # . clear-stream(_test-output-stream) +4437 # . . push args +4438 68/push _test-output-stream/imm32 +4439 # . . call +4440 e8/call clear-stream/disp32 +4441 # . . discard args +4442 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4443 # . clear-stream(_test-output-buffered-file+4) +4444 # . . push args +4445 b8/copy-to-EAX _test-output-buffered-file/imm32 +4446 05/add-to-EAX 4/imm32 +4447 50/push-EAX +4448 # . . call +4449 e8/call clear-stream/disp32 +4450 # . . discard args +4451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4452 # initialize input +4453 # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32") +4454 # . . push args +4455 68/push "ff 6/subop/push 0/mod 0/rm32"/imm32 +4456 68/push _test-input-stream/imm32 +4457 # . . call +4458 e8/call write/disp32 +4459 # . . discard args +4460 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4461 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4462 # . . push args +4463 68/push _test-output-buffered-file/imm32 +4464 68/push _test-input-stream/imm32 +4465 # . . call +4466 e8/call convert-instruction/disp32 +4467 # . . discard args +4468 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4469 # check output +4470 # . flush(_test-output-buffered-file) +4471 # . . push args +4472 68/push _test-output-buffered-file/imm32 +4473 # . . call +4474 e8/call flush/disp32 +4475 # . . discard args +4476 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4477 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4503 # . check-stream-equal(_test-output-stream, "ff 30 # ff 6/subop/push 0/mod 0/rm32", msg) +4504 # . . push args +4505 68/push "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32 +4506 68/push "ff 30 # ff 6/subop/push 0/mod 0/rm32"/imm32 +4507 68/push _test-output-stream/imm32 +4508 # . . call +4509 e8/call check-stream-equal/disp32 +4510 # . . discard args +4511 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4512 # . epilog +4513 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4514 5d/pop-to-EBP +4515 c3/return +4516 +4517 test-convert-instruction-emits-modrm-byte-with-missing-mod: +4518 # pack rm32 and r32 operands into ModR/M byte +4519 # . prolog +4520 55/push-EBP +4521 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4522 # setup +4523 # . clear-stream(_test-input-stream) +4524 # . . push args +4525 68/push _test-input-stream/imm32 +4526 # . . call +4527 e8/call clear-stream/disp32 +4528 # . . discard args +4529 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4530 # . clear-stream(_test-output-stream) +4531 # . . push args +4532 68/push _test-output-stream/imm32 +4533 # . . call +4534 e8/call clear-stream/disp32 +4535 # . . discard args +4536 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4537 # . clear-stream(_test-output-buffered-file+4) +4538 # . . push args +4539 b8/copy-to-EAX _test-output-buffered-file/imm32 +4540 05/add-to-EAX 4/imm32 +4541 50/push-EAX +4542 # . . call +4543 e8/call clear-stream/disp32 +4544 # . . discard args +4545 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4546 # initialize input +4547 # . write(_test-input-stream, "8b/copy 0/rm32 1/r32") +4548 # . . push args +4549 68/push "8b/copy 0/rm32 1/r32"/imm32 +4550 68/push _test-input-stream/imm32 +4551 # . . call +4552 e8/call write/disp32 +4553 # . . discard args +4554 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4555 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4556 # . . push args +4557 68/push _test-output-buffered-file/imm32 +4558 68/push _test-input-stream/imm32 +4559 # . . call +4560 e8/call convert-instruction/disp32 +4561 # . . discard args +4562 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4563 # check output +4564 # . flush(_test-output-buffered-file) +4565 # . . push args +4566 68/push _test-output-buffered-file/imm32 +4567 # . . call +4568 e8/call flush/disp32 +4569 # . . discard args +4570 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4571 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4597 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/rm32 1/r32", msg) +4598 # . . push args +4599 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32 +4600 68/push "8b 08 # 8b/copy 0/rm32 1/r32"/imm32 +4601 68/push _test-output-stream/imm32 +4602 # . . call +4603 e8/call check-stream-equal/disp32 +4604 # . . discard args +4605 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4606 # . epilog +4607 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4608 5d/pop-to-EBP +4609 c3/return +4610 +4611 test-convert-instruction-emits-modrm-byte-with-missing-rm32: +4612 # pack mod and r32 operands into ModR/M byte +4613 # . prolog +4614 55/push-EBP +4615 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4616 # setup +4617 # . clear-stream(_test-input-stream) +4618 # . . push args +4619 68/push _test-input-stream/imm32 +4620 # . . call +4621 e8/call clear-stream/disp32 +4622 # . . discard args +4623 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4624 # . clear-stream(_test-output-stream) +4625 # . . push args +4626 68/push _test-output-stream/imm32 +4627 # . . call +4628 e8/call clear-stream/disp32 +4629 # . . discard args +4630 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4631 # . clear-stream(_test-output-buffered-file+4) +4632 # . . push args +4633 b8/copy-to-EAX _test-output-buffered-file/imm32 +4634 05/add-to-EAX 4/imm32 +4635 50/push-EAX +4636 # . . call +4637 e8/call clear-stream/disp32 +4638 # . . discard args +4639 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4640 # initialize input +4641 # . write(_test-input-stream, "8b/copy 0/mod 1/r32") +4642 # . . push args +4643 68/push "8b/copy 0/mod 1/r32"/imm32 +4644 68/push _test-input-stream/imm32 +4645 # . . call +4646 e8/call write/disp32 +4647 # . . discard args +4648 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4649 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4650 # . . push args +4651 68/push _test-output-buffered-file/imm32 +4652 68/push _test-input-stream/imm32 +4653 # . . call +4654 e8/call convert-instruction/disp32 +4655 # . . discard args +4656 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4657 # check output +4658 # . flush(_test-output-buffered-file) +4659 # . . push args +4660 68/push _test-output-buffered-file/imm32 +4661 # . . call +4662 e8/call flush/disp32 +4663 # . . discard args +4664 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4665 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4691 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 1/r32", msg) +4692 # . . push args +4693 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32 +4694 68/push "8b 08 # 8b/copy 0/mod 1/r32"/imm32 +4695 68/push _test-output-stream/imm32 +4696 # . . call +4697 e8/call check-stream-equal/disp32 +4698 # . . discard args +4699 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4700 # . epilog +4701 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4702 5d/pop-to-EBP +4703 c3/return +4704 +4705 test-convert-instruction-emits-modrm-byte-with-missing-r32: +4706 # pack mod and rm32 operands into ModR/M byte +4707 # . prolog +4708 55/push-EBP +4709 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4710 # setup +4711 # . clear-stream(_test-input-stream) +4712 # . . push args +4713 68/push _test-input-stream/imm32 +4714 # . . call +4715 e8/call clear-stream/disp32 +4716 # . . discard args +4717 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4718 # . clear-stream(_test-output-stream) +4719 # . . push args +4720 68/push _test-output-stream/imm32 +4721 # . . call +4722 e8/call clear-stream/disp32 +4723 # . . discard args +4724 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4725 # . clear-stream(_test-output-buffered-file+4) +4726 # . . push args +4727 b8/copy-to-EAX _test-output-buffered-file/imm32 +4728 05/add-to-EAX 4/imm32 +4729 50/push-EAX +4730 # . . call +4731 e8/call clear-stream/disp32 +4732 # . . discard args +4733 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4734 # initialize input +4735 # . write(_test-input-stream, "8b/copy 0/mod 0/rm32") +4736 # . . push args +4737 68/push "8b/copy 0/mod 0/rm32"/imm32 +4738 68/push _test-input-stream/imm32 +4739 # . . call +4740 e8/call write/disp32 +4741 # . . discard args +4742 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4743 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4744 # . . push args +4745 68/push _test-output-buffered-file/imm32 +4746 68/push _test-input-stream/imm32 +4747 # . . call +4748 e8/call convert-instruction/disp32 +4749 # . . discard args +4750 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4751 # check output +4752 # . flush(_test-output-buffered-file) +4753 # . . push args +4754 68/push _test-output-buffered-file/imm32 +4755 # . . call +4756 e8/call flush/disp32 +4757 # . . discard args +4758 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4759 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4785 # . check-stream-equal(_test-output-stream, "8b 00 # 8b/copy 0/mod 0/rm32", msg) +4786 # . . push args +4787 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32 +4788 68/push "8b 00 # 8b/copy 0/mod 0/rm32"/imm32 +4789 68/push _test-output-stream/imm32 +4790 # . . call +4791 e8/call check-stream-equal/disp32 +4792 # . . discard args +4793 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4794 # . epilog +4795 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4796 5d/pop-to-EBP +4797 c3/return +4798 +4799 test-convert-instruction-emits-sib-byte: +4800 # pack base, index and scale operands into SIB byte +4801 # . prolog +4802 55/push-EBP +4803 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4804 # setup +4805 # . clear-stream(_test-input-stream) +4806 # . . push args +4807 68/push _test-input-stream/imm32 +4808 # . . call +4809 e8/call clear-stream/disp32 +4810 # . . discard args +4811 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4812 # . clear-stream(_test-output-stream) +4813 # . . push args +4814 68/push _test-output-stream/imm32 +4815 # . . call +4816 e8/call clear-stream/disp32 +4817 # . . discard args +4818 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4819 # . clear-stream(_test-output-buffered-file+4) +4820 # . . push args +4821 b8/copy-to-EAX _test-output-buffered-file/imm32 +4822 05/add-to-EAX 4/imm32 +4823 50/push-EAX +4824 # . . call +4825 e8/call clear-stream/disp32 +4826 # . . discard args +4827 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4828 # initialize input +4829 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale") +4830 # . . push args +4831 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32 +4832 68/push _test-input-stream/imm32 +4833 # . . call +4834 e8/call write/disp32 +4835 # . . discard args +4836 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4837 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4838 # . . push args +4839 68/push _test-output-buffered-file/imm32 +4840 68/push _test-input-stream/imm32 +4841 # . . call +4842 e8/call convert-instruction/disp32 +4843 # . . discard args +4844 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4845 # check output +4846 # . flush(_test-output-buffered-file) +4847 # . . push args +4848 68/push _test-output-buffered-file/imm32 +4849 # . . call +4850 e8/call flush/disp32 +4851 # . . discard args +4852 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4853 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4879 # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg) +4880 # . . push args +4881 68/push "F - test-convert-instruction-emits-sib-byte"/imm32 +4882 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32 +4883 68/push _test-output-stream/imm32 +4884 # . . call +4885 e8/call check-stream-equal/disp32 +4886 # . . discard args +4887 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4888 # . epilog +4889 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4890 5d/pop-to-EBP +4891 c3/return +4892 +4893 test-convert-instruction-emits-sib-byte-with-missing-base: +4894 # pack index and scale operands into SIB byte +4895 # . prolog +4896 55/push-EBP +4897 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4898 # setup +4899 # . clear-stream(_test-input-stream) +4900 # . . push args +4901 68/push _test-input-stream/imm32 +4902 # . . call +4903 e8/call clear-stream/disp32 +4904 # . . discard args +4905 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4906 # . clear-stream(_test-output-stream) +4907 # . . push args +4908 68/push _test-output-stream/imm32 +4909 # . . call +4910 e8/call clear-stream/disp32 +4911 # . . discard args +4912 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4913 # . clear-stream(_test-output-buffered-file+4) +4914 # . . push args +4915 b8/copy-to-EAX _test-output-buffered-file/imm32 +4916 05/add-to-EAX 4/imm32 +4917 50/push-EAX +4918 # . . call +4919 e8/call clear-stream/disp32 +4920 # . . discard args +4921 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4922 # initialize input +4923 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale") +4924 # . . push args +4925 68/push "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32 +4926 68/push _test-input-stream/imm32 +4927 # . . call +4928 e8/call write/disp32 +4929 # . . discard args +4930 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4931 # convert-instruction(_test-input-stream, _test-output-buffered-file) +4932 # . . push args +4933 68/push _test-output-buffered-file/imm32 +4934 68/push _test-input-stream/imm32 +4935 # . . call +4936 e8/call convert-instruction/disp32 +4937 # . . discard args +4938 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +4939 # check output +4940 # . flush(_test-output-buffered-file) +4941 # . . push args +4942 68/push _test-output-buffered-file/imm32 +4943 # . . call +4944 e8/call flush/disp32 +4945 # . . discard args +4946 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +4947 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +4973 # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg) +4974 # . . push args +4975 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32 +4976 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32 +4977 68/push _test-output-stream/imm32 +4978 # . . call +4979 e8/call check-stream-equal/disp32 +4980 # . . discard args +4981 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +4982 # . epilog +4983 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +4984 5d/pop-to-EBP +4985 c3/return +4986 +4987 test-convert-instruction-emits-sib-byte-with-missing-index: +4988 # pack base and scale operands into SIB byte +4989 # . prolog +4990 55/push-EBP +4991 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +4992 # setup +4993 # . clear-stream(_test-input-stream) +4994 # . . push args +4995 68/push _test-input-stream/imm32 +4996 # . . call +4997 e8/call clear-stream/disp32 +4998 # . . discard args +4999 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5000 # . clear-stream(_test-output-stream) +5001 # . . push args +5002 68/push _test-output-stream/imm32 +5003 # . . call +5004 e8/call clear-stream/disp32 +5005 # . . discard args +5006 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5007 # . clear-stream(_test-output-buffered-file+4) +5008 # . . push args +5009 b8/copy-to-EAX _test-output-buffered-file/imm32 +5010 05/add-to-EAX 4/imm32 +5011 50/push-EAX +5012 # . . call +5013 e8/call clear-stream/disp32 +5014 # . . discard args +5015 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5016 # initialize input +5017 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale") +5018 # . . push args +5019 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32 +5020 68/push _test-input-stream/imm32 +5021 # . . call +5022 e8/call write/disp32 +5023 # . . discard args +5024 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5025 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5026 # . . push args +5027 68/push _test-output-buffered-file/imm32 +5028 68/push _test-input-stream/imm32 +5029 # . . call +5030 e8/call convert-instruction/disp32 +5031 # . . discard args +5032 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5033 # check output +5034 # . flush(_test-output-buffered-file) +5035 # . . push args +5036 68/push _test-output-buffered-file/imm32 +5037 # . . call +5038 e8/call flush/disp32 +5039 # . . discard args +5040 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5041 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5067 # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg) +5068 # . . push args +5069 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32 +5070 68/push "8b 0c 00 # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32 +5071 68/push _test-output-stream/imm32 +5072 # . . call +5073 e8/call check-stream-equal/disp32 +5074 # . . discard args +5075 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5076 # . epilog +5077 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5078 5d/pop-to-EBP +5079 c3/return +5080 +5081 test-convert-instruction-emits-sib-byte-with-missing-scale: +5082 # pack base and index operands into SIB byte +5083 # . prolog +5084 55/push-EBP +5085 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5086 # setup +5087 # . clear-stream(_test-input-stream) +5088 # . . push args +5089 68/push _test-input-stream/imm32 +5090 # . . call +5091 e8/call clear-stream/disp32 +5092 # . . discard args +5093 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5094 # . clear-stream(_test-output-stream) +5095 # . . push args +5096 68/push _test-output-stream/imm32 +5097 # . . call +5098 e8/call clear-stream/disp32 +5099 # . . discard args +5100 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5101 # . clear-stream(_test-output-buffered-file+4) +5102 # . . push args +5103 b8/copy-to-EAX _test-output-buffered-file/imm32 +5104 05/add-to-EAX 4/imm32 +5105 50/push-EAX +5106 # . . call +5107 e8/call clear-stream/disp32 +5108 # . . discard args +5109 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5110 # initialize input +5111 # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index") +5112 # . . push args +5113 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32 +5114 68/push _test-input-stream/imm32 +5115 # . . call +5116 e8/call write/disp32 +5117 # . . discard args +5118 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5119 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5120 # . . push args +5121 68/push _test-output-buffered-file/imm32 +5122 68/push _test-input-stream/imm32 +5123 # . . call +5124 e8/call convert-instruction/disp32 +5125 # . . discard args +5126 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5127 # check output +5128 # . flush(_test-output-buffered-file) +5129 # . . push args +5130 68/push _test-output-buffered-file/imm32 +5131 # . . call +5132 e8/call flush/disp32 +5133 # . . discard args +5134 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5135 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5161 # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg) +5162 # . . push args +5163 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32 +5164 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32 +5165 68/push _test-output-stream/imm32 +5166 # . . call +5167 e8/call check-stream-equal/disp32 +5168 # . . discard args +5169 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5170 # . epilog +5171 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5172 5d/pop-to-EBP +5173 c3/return +5174 +5175 test-convert-instruction-handles-disp32-operand: +5176 # expand /disp32 operand into 4 bytes +5177 # . prolog +5178 55/push-EBP +5179 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5180 # setup +5181 # . clear-stream(_test-input-stream) +5182 # . . push args +5183 68/push _test-input-stream/imm32 +5184 # . . call +5185 e8/call clear-stream/disp32 +5186 # . . discard args +5187 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5188 # . clear-stream(_test-output-stream) +5189 # . . push args +5190 68/push _test-output-stream/imm32 +5191 # . . call +5192 e8/call clear-stream/disp32 +5193 # . . discard args +5194 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5195 # . clear-stream(_test-output-buffered-file+4) +5196 # . . push args +5197 b8/copy-to-EAX _test-output-buffered-file/imm32 +5198 05/add-to-EAX 4/imm32 +5199 50/push-EAX +5200 # . . call +5201 e8/call clear-stream/disp32 +5202 # . . discard args +5203 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5204 # initialize input +5205 # . write(_test-input-stream, "e8/call 20/disp32") +5206 # . . push args +5207 68/push "e8/call 20/disp32"/imm32 +5208 68/push _test-input-stream/imm32 +5209 # . . call +5210 e8/call write/disp32 +5211 # . . discard args +5212 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5213 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5214 # . . push args +5215 68/push _test-output-buffered-file/imm32 +5216 68/push _test-input-stream/imm32 +5217 # . . call +5218 e8/call convert-instruction/disp32 +5219 # . . discard args +5220 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5221 # check output +5222 # . flush(_test-output-buffered-file) +5223 # . . push args +5224 68/push _test-output-buffered-file/imm32 +5225 # . . call +5226 e8/call flush/disp32 +5227 # . . discard args +5228 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5229 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5255 # . check-stream-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg) +5256 # . . push args +5257 68/push "F - test-convert-instruction-handles-disp32-operand"/imm32 +5258 68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32 +5259 68/push _test-output-stream/imm32 +5260 # . . call +5261 e8/call check-stream-equal/disp32 +5262 # . . discard args +5263 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5264 # . epilog +5265 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5266 5d/pop-to-EBP +5267 c3/return +5268 +5269 test-convert-instruction-handles-disp16-operand: +5270 # expand /disp16 operand into 2 bytes +5271 # . prolog +5272 55/push-EBP +5273 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5274 # setup +5275 # . clear-stream(_test-input-stream) +5276 # . . push args +5277 68/push _test-input-stream/imm32 +5278 # . . call +5279 e8/call clear-stream/disp32 +5280 # . . discard args +5281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5282 # . clear-stream(_test-output-stream) +5283 # . . push args +5284 68/push _test-output-stream/imm32 +5285 # . . call +5286 e8/call clear-stream/disp32 +5287 # . . discard args +5288 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5289 # . clear-stream(_test-output-buffered-file+4) +5290 # . . push args +5291 b8/copy-to-EAX _test-output-buffered-file/imm32 +5292 05/add-to-EAX 4/imm32 +5293 50/push-EAX +5294 # . . call +5295 e8/call clear-stream/disp32 +5296 # . . discard args +5297 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5298 # initialize input +5299 # . write(_test-input-stream, "e8/call 20/disp16") +5300 # . . push args +5301 68/push "e8/call 20/disp16"/imm32 # not a valid instruction +5302 68/push _test-input-stream/imm32 +5303 # . . call +5304 e8/call write/disp32 +5305 # . . discard args +5306 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5307 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5308 # . . push args +5309 68/push _test-output-buffered-file/imm32 +5310 68/push _test-input-stream/imm32 +5311 # . . call +5312 e8/call convert-instruction/disp32 +5313 # . . discard args +5314 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5315 # check output +5316 # . flush(_test-output-buffered-file) +5317 # . . push args +5318 68/push _test-output-buffered-file/imm32 +5319 # . . call +5320 e8/call flush/disp32 +5321 # . . discard args +5322 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5323 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5349 # . check-stream-equal(_test-output-stream, "e8 20 00 # e8/call 20/disp16", msg) +5350 # . . push args +5351 68/push "F - test-convert-instruction-handles-disp16-operand"/imm32 +5352 68/push "e8 20 00 # e8/call 20/disp16"/imm32 +5353 68/push _test-output-stream/imm32 +5354 # . . call +5355 e8/call check-stream-equal/disp32 +5356 # . . discard args +5357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5358 # . epilog +5359 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5360 5d/pop-to-EBP +5361 c3/return +5362 +5363 test-convert-instruction-handles-disp8-operand: +5364 # expand /disp8 operand into 1 byte +5365 # . prolog +5366 55/push-EBP +5367 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5368 # setup +5369 # . clear-stream(_test-input-stream) +5370 # . . push args +5371 68/push _test-input-stream/imm32 +5372 # . . call +5373 e8/call clear-stream/disp32 +5374 # . . discard args +5375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5376 # . clear-stream(_test-output-stream) +5377 # . . push args +5378 68/push _test-output-stream/imm32 +5379 # . . call +5380 e8/call clear-stream/disp32 +5381 # . . discard args +5382 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5383 # . clear-stream(_test-output-buffered-file+4) +5384 # . . push args +5385 b8/copy-to-EAX _test-output-buffered-file/imm32 +5386 05/add-to-EAX 4/imm32 +5387 50/push-EAX +5388 # . . call +5389 e8/call clear-stream/disp32 +5390 # . . discard args +5391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5392 # initialize input +5393 # . write(_test-input-stream, "eb/jump 20/disp8") +5394 # . . push args +5395 68/push "eb/jump 20/disp8"/imm32 +5396 68/push _test-input-stream/imm32 +5397 # . . call +5398 e8/call write/disp32 +5399 # . . discard args +5400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5401 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5402 # . . push args +5403 68/push _test-output-buffered-file/imm32 +5404 68/push _test-input-stream/imm32 +5405 # . . call +5406 e8/call convert-instruction/disp32 +5407 # . . discard args +5408 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5409 # check output +5410 # . flush(_test-output-buffered-file) +5411 # . . push args +5412 68/push _test-output-buffered-file/imm32 +5413 # . . call +5414 e8/call flush/disp32 +5415 # . . discard args +5416 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5417 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5443 # . check-stream-equal(_test-output-stream, "eb 20 # eb/jump 20/disp8", msg) +5444 # . . push args +5445 68/push "F - test-convert-instruction-handles-disp8-operand"/imm32 +5446 68/push "eb 20 # eb/jump 20/disp8"/imm32 +5447 68/push _test-output-stream/imm32 +5448 # . . call +5449 e8/call check-stream-equal/disp32 +5450 # . . discard args +5451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5452 # . epilog +5453 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5454 5d/pop-to-EBP +5455 c3/return +5456 +5457 test-convert-instruction-handles-disp8-name: +5458 # pass /disp8 name directly through +5459 # . prolog +5460 55/push-EBP +5461 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5462 # setup +5463 # . clear-stream(_test-input-stream) +5464 # . . push args +5465 68/push _test-input-stream/imm32 +5466 # . . call +5467 e8/call clear-stream/disp32 +5468 # . . discard args +5469 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5470 # . clear-stream(_test-output-stream) +5471 # . . push args +5472 68/push _test-output-stream/imm32 +5473 # . . call +5474 e8/call clear-stream/disp32 +5475 # . . discard args +5476 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5477 # . clear-stream(_test-output-buffered-file+4) +5478 # . . push args +5479 b8/copy-to-EAX _test-output-buffered-file/imm32 +5480 05/add-to-EAX 4/imm32 +5481 50/push-EAX +5482 # . . call +5483 e8/call clear-stream/disp32 +5484 # . . discard args +5485 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5486 # initialize input +5487 # . write(_test-input-stream, "eb/jump xyz/disp8") +5488 # . . push args +5489 68/push "eb/jump xyz/disp8"/imm32 +5490 68/push _test-input-stream/imm32 +5491 # . . call +5492 e8/call write/disp32 +5493 # . . discard args +5494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5495 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5496 # . . push args +5497 68/push _test-output-buffered-file/imm32 +5498 68/push _test-input-stream/imm32 +5499 # . . call +5500 e8/call convert-instruction/disp32 +5501 # . . discard args +5502 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5503 # check output +5504 # . flush(_test-output-buffered-file) +5505 # . . push args +5506 68/push _test-output-buffered-file/imm32 +5507 # . . call +5508 e8/call flush/disp32 +5509 # . . discard args +5510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5511 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5537 # . check-stream-equal(_test-output-stream, "eb xyz/disp8 # eb/jump xyz/disp8", msg) +5538 # . . push args +5539 68/push "F - test-convert-instruction-handles-disp8-name"/imm32 +5540 68/push "eb xyz/disp8 # eb/jump xyz/disp8"/imm32 +5541 68/push _test-output-stream/imm32 +5542 # . . call +5543 e8/call check-stream-equal/disp32 +5544 # . . discard args +5545 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5546 # . epilog +5547 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5548 5d/pop-to-EBP +5549 c3/return +5550 +5551 test-convert-instruction-handles-imm32-operand: +5552 # expand /imm32 operand into 4 bytes +5553 # . prolog +5554 55/push-EBP +5555 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5556 # setup +5557 # . clear-stream(_test-input-stream) +5558 # . . push args +5559 68/push _test-input-stream/imm32 +5560 # . . call +5561 e8/call clear-stream/disp32 +5562 # . . discard args +5563 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5564 # . clear-stream(_test-output-stream) +5565 # . . push args +5566 68/push _test-output-stream/imm32 +5567 # . . call +5568 e8/call clear-stream/disp32 +5569 # . . discard args +5570 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5571 # . clear-stream(_test-output-buffered-file+4) +5572 # . . push args +5573 b8/copy-to-EAX _test-output-buffered-file/imm32 +5574 05/add-to-EAX 4/imm32 +5575 50/push-EAX +5576 # . . call +5577 e8/call clear-stream/disp32 +5578 # . . discard args +5579 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5580 # initialize input +5581 # . write(_test-input-stream, "68/push 0x20/imm32") +5582 # . . push args +5583 68/push "68/push 0x20/imm32"/imm32 +5584 68/push _test-input-stream/imm32 +5585 # . . call +5586 e8/call write/disp32 +5587 # . . discard args +5588 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5589 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5590 # . . push args +5591 68/push _test-output-buffered-file/imm32 +5592 68/push _test-input-stream/imm32 +5593 # . . call +5594 e8/call convert-instruction/disp32 +5595 # . . discard args +5596 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5597 # check output +5598 # . flush(_test-output-buffered-file) +5599 # . . push args +5600 68/push _test-output-buffered-file/imm32 +5601 # . . call +5602 e8/call flush/disp32 +5603 # . . discard args +5604 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5605 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5631 # . check-stream-equal(_test-output-stream, "68 20 00 00 00 # 68/push 0x20/imm32", msg) +5632 # . . push args +5633 68/push "F - test-convert-instruction-handles-imm32-operand"/imm32 +5634 68/push "68 20 00 00 00 # 68/push 0x20/imm32"/imm32 +5635 68/push _test-output-stream/imm32 +5636 # . . call +5637 e8/call check-stream-equal/disp32 +5638 # . . discard args +5639 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5640 # . epilog +5641 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5642 5d/pop-to-EBP +5643 c3/return +5644 +5645 test-convert-instruction-handles-imm16-operand: +5646 # expand /imm16 operand into 2 bytes +5647 # we don't have one of these at the moment, so this expands to an invalid instruction +5648 # . prolog +5649 55/push-EBP +5650 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5651 # setup +5652 # . clear-stream(_test-input-stream) +5653 # . . push args +5654 68/push _test-input-stream/imm32 +5655 # . . call +5656 e8/call clear-stream/disp32 +5657 # . . discard args +5658 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5659 # . clear-stream(_test-output-stream) +5660 # . . push args +5661 68/push _test-output-stream/imm32 +5662 # . . call +5663 e8/call clear-stream/disp32 +5664 # . . discard args +5665 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5666 # . clear-stream(_test-output-buffered-file+4) +5667 # . . push args +5668 b8/copy-to-EAX _test-output-buffered-file/imm32 +5669 05/add-to-EAX 4/imm32 +5670 50/push-EAX +5671 # . . call +5672 e8/call clear-stream/disp32 +5673 # . . discard args +5674 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5675 # initialize input +5676 # . write(_test-input-stream, "68/push 0x20/imm16") +5677 # . . push args +5678 68/push "68/push 0x20/imm16"/imm32 # not a valid instruction +5679 68/push _test-input-stream/imm32 +5680 # . . call +5681 e8/call write/disp32 +5682 # . . discard args +5683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5684 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5685 # . . push args +5686 68/push _test-output-buffered-file/imm32 +5687 68/push _test-input-stream/imm32 +5688 # . . call +5689 e8/call convert-instruction/disp32 +5690 # . . discard args +5691 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5692 # check output +5693 # . flush(_test-output-buffered-file) +5694 # . . push args +5695 68/push _test-output-buffered-file/imm32 +5696 # . . call +5697 e8/call flush/disp32 +5698 # . . discard args +5699 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5700 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5726 # . check-stream-equal(_test-output-stream, "68 20 00 # 68/push 0x20/imm16", msg) +5727 # . . push args +5728 68/push "F - test-convert-instruction-handles-imm16-operand"/imm32 +5729 68/push "68 20 00 # 68/push 0x20/imm16"/imm32 +5730 68/push _test-output-stream/imm32 +5731 # . . call +5732 e8/call check-stream-equal/disp32 +5733 # . . discard args +5734 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5735 # . epilog +5736 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5737 5d/pop-to-EBP +5738 c3/return +5739 +5740 test-convert-instruction-handles-imm8-operand: +5741 # expand /imm8 operand into 1 byte +5742 # we don't have one of these at the moment, so this expands to an invalid instruction +5743 # . prolog +5744 55/push-EBP +5745 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +5746 # setup +5747 # . clear-stream(_test-input-stream) +5748 # . . push args +5749 68/push _test-input-stream/imm32 +5750 # . . call +5751 e8/call clear-stream/disp32 +5752 # . . discard args +5753 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5754 # . clear-stream(_test-output-stream) +5755 # . . push args +5756 68/push _test-output-stream/imm32 +5757 # . . call +5758 e8/call clear-stream/disp32 +5759 # . . discard args +5760 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5761 # . clear-stream(_test-output-buffered-file+4) +5762 # . . push args +5763 b8/copy-to-EAX _test-output-buffered-file/imm32 +5764 05/add-to-EAX 4/imm32 +5765 50/push-EAX +5766 # . . call +5767 e8/call clear-stream/disp32 +5768 # . . discard args +5769 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5770 # initialize input +5771 # . write(_test-input-stream, "68/push 0x20/imm8") +5772 # . . push args +5773 68/push "68/push 0x20/imm8"/imm32 +5774 68/push _test-input-stream/imm32 +5775 # . . call +5776 e8/call write/disp32 +5777 # . . discard args +5778 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5779 # convert-instruction(_test-input-stream, _test-output-buffered-file) +5780 # . . push args +5781 68/push _test-output-buffered-file/imm32 +5782 68/push _test-input-stream/imm32 +5783 # . . call +5784 e8/call convert-instruction/disp32 +5785 # . . discard args +5786 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5787 # check output +5788 # . flush(_test-output-buffered-file) +5789 # . . push args +5790 68/push _test-output-buffered-file/imm32 +5791 # . . call +5792 e8/call flush/disp32 +5793 # . . discard args +5794 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5795 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +5821 # . check-stream-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg) +5822 # . . push args +5823 68/push "F - test-convert-instruction-handles-imm8-operand"/imm32 +5824 68/push "68 20 # 68/push 0x20/imm8"/imm32 +5825 68/push _test-output-stream/imm32 +5826 # . . call +5827 e8/call check-stream-equal/disp32 +5828 # . . discard args +5829 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +5830 # . epilog +5831 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5832 5d/pop-to-EBP +5833 c3/return +5834 +5835 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/')) +5836 parse-datum-of-word: # word : (address slice) -> value/EAX 5837 # . prolog 5838 55/push-EBP 5839 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 5840 # . save registers -5841 50/push-EAX -5842 51/push-ECX -5843 56/push-ESI -5844 57/push-EDI -5845 # ESI = line -5846 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -5847 # EDI = out -5848 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -5849 # skip-chars-matching(line, ' ') +5841 51/push-ECX +5842 56/push-ESI +5843 # ESI = word +5844 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +5845 # var slice/ECX = {0, 0} +5846 68/push 0/imm32/end +5847 68/push 0/imm32/start +5848 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +5849 # slice = next-token-from-slice(word->start, word->end, '/') 5850 # . . push args -5851 68/push 0x20/imm32/space -5852 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -5853 # . . call -5854 e8/call skip-chars-matching/disp32 -5855 # . . discard args -5856 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5857 $next-word:check0: -5858 # if (line->read >= line->write) clear out and return -5859 # . EAX = line->read -5860 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX -5861 # . if (EAX < line->write) goto next check -5862 3b/compare 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # compare EAX with *ESI -5863 7c/jump-if-lesser $next-word:check-for-comment/disp8 -5864 # . return out = {0, 0} -5865 c7 0/subop/copy 0/mod/direct 7/rm32/EDI . . . . . 0/imm32 # copy to *EDI -5866 c7 0/subop/copy 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 0/imm32 # copy to *(EDI+4) -5867 eb/jump $next-word:end/disp8 -5868 $next-word:check-for-comment: -5869 # out->start = &line->data[line->read] -5870 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX -5871 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX -5872 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI -5873 # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return -5874 # . EAX = line->data[line->read] -5875 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -5876 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL -5877 # . compare -5878 3d/compare-EAX-and 0x23/imm32/pound -5879 75/jump-if-not-equal $next-word:regular-word/disp8 -5880 $next-word:comment: -5881 # . out->end = &line->data[line->write] -5882 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX -5883 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX -5884 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -5885 # . line->read = line->write -5886 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) -5887 # . return -5888 eb/jump $next-word:end/disp8 -5889 $next-word:regular-word: -5890 # otherwise skip-chars-not-matching-whitespace(line) # including trailing newline -5891 # . . push args -5892 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -5893 # . . call -5894 e8/call skip-chars-not-matching-whitespace/disp32 -5895 # . . discard args -5896 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5897 # out->end = &line->data[line->read] -5898 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX -5899 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX -5900 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -5901 $next-word:end: -5902 # . restore registers -5903 5f/pop-to-EDI -5904 5e/pop-to-ESI -5905 59/pop-to-ECX -5906 58/pop-to-EAX -5907 # . epilog -5908 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5909 5d/pop-to-EBP -5910 c3/return -5911 -5912 test-next-word: -5913 # . prolog -5914 55/push-EBP -5915 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5916 # setup -5917 # . clear-stream(_test-stream) -5918 # . . push args -5919 68/push _test-stream/imm32 -5920 # . . call -5921 e8/call clear-stream/disp32 -5922 # . . discard args -5923 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5924 # var slice/ECX = {0, 0} -5925 68/push 0/imm32/end -5926 68/push 0/imm32/start -5927 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -5928 # write(_test-stream, " ab") -5929 # . . push args -5930 68/push " ab"/imm32 -5931 68/push _test-stream/imm32 -5932 # . . call -5933 e8/call write/disp32 -5934 # . . discard args -5935 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5936 # next-word(_test-stream, slice) -5937 # . . push args -5938 51/push-ECX -5939 68/push _test-stream/imm32 -5940 # . . call -5941 e8/call next-word/disp32 -5942 # . . discard args -5943 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5944 # check-ints-equal(slice->start - _test-stream->data, 2, msg) -5945 # . check-ints-equal(slice->start - _test-stream, 14, msg) -5946 # . . push args -5947 68/push "F - test-next-word: start"/imm32 -5948 68/push 0xe/imm32 -5949 # . . push slice->start - _test-stream -5950 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -5951 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -5952 50/push-EAX -5953 # . . call -5954 e8/call check-ints-equal/disp32 -5955 # . . discard args -5956 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5957 # check-ints-equal(slice->end - _test-stream->data, 4, msg) -5958 # . check-ints-equal(slice->end - _test-stream, 16, msg) -5959 # . . push args -5960 68/push "F - test-next-word: end"/imm32 -5961 68/push 0x10/imm32 -5962 # . . push slice->end - _test-stream -5963 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -5964 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -5965 50/push-EAX -5966 # . . call -5967 e8/call check-ints-equal/disp32 -5968 # . . discard args -5969 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -5970 # . epilog -5971 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -5972 5d/pop-to-EBP -5973 c3/return -5974 -5975 test-next-word-returns-whole-comment: -5976 # . prolog -5977 55/push-EBP -5978 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -5979 # setup -5980 # . clear-stream(_test-stream) -5981 # . . push args -5982 68/push _test-stream/imm32 -5983 # . . call -5984 e8/call clear-stream/disp32 -5985 # . . discard args -5986 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -5987 # var slice/ECX = {0, 0} -5988 68/push 0/imm32/end -5989 68/push 0/imm32/start -5990 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -5991 # write(_test-stream, " # a") -5992 # . . push args -5993 68/push " # a"/imm32 -5994 68/push _test-stream/imm32 -5995 # . . call -5996 e8/call write/disp32 -5997 # . . discard args -5998 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -5999 # next-word(_test-stream, slice) -6000 # . . push args -6001 51/push-ECX -6002 68/push _test-stream/imm32 -6003 # . . call -6004 e8/call next-word/disp32 -6005 # . . discard args -6006 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6007 # check-ints-equal(slice->start - _test-stream->data, 2, msg) -6008 # . check-ints-equal(slice->start - _test-stream, 14, msg) -6009 # . . push args -6010 68/push "F - test-next-word-returns-whole-comment: start"/imm32 -6011 68/push 0xe/imm32 -6012 # . . push slice->start - _test-stream -6013 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -6014 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -6015 50/push-EAX -6016 # . . call -6017 e8/call check-ints-equal/disp32 -6018 # . . discard args -6019 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6020 # check-ints-equal(slice->end - _test-stream->data, 5, msg) -6021 # . check-ints-equal(slice->end - _test-stream, 17, msg) -6022 # . . push args -6023 68/push "F - test-next-word-returns-whole-comment: end"/imm32 -6024 68/push 0x11/imm32 -6025 # . . push slice->end - _test-stream -6026 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -6027 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -6028 50/push-EAX -6029 # . . call -6030 e8/call check-ints-equal/disp32 -6031 # . . discard args -6032 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6033 # . epilog -6034 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6035 5d/pop-to-EBP -6036 c3/return -6037 -6038 test-next-word-returns-empty-string-on-eof: -6039 # . prolog -6040 55/push-EBP -6041 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6042 # setup -6043 # . clear-stream(_test-stream) -6044 # . . push args -6045 68/push _test-stream/imm32 -6046 # . . call -6047 e8/call clear-stream/disp32 -6048 # . . discard args -6049 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6050 # var slice/ECX = {0, 0} -6051 68/push 0/imm32/end -6052 68/push 0/imm32/start -6053 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6054 # write nothing to _test-stream -6055 # next-word(_test-stream, slice) -6056 # . . push args -6057 51/push-ECX -6058 68/push _test-stream/imm32 -6059 # . . call -6060 e8/call next-word/disp32 -6061 # . . discard args -6062 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6063 # check-ints-equal(slice->end - slice->start, 0, msg) -6064 # . . push args -6065 68/push "F - test-next-word-returns-empty-string-on-eof"/imm32 -6066 68/push 0/imm32 -6067 # . . push slice->end - slice->start -6068 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -6069 2b/subtract 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # subtract *ECX from EAX -6070 50/push-EAX -6071 # . . call -6072 e8/call check-ints-equal/disp32 -6073 # . . discard args -6074 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6075 # . epilog -6076 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6077 5d/pop-to-EBP -6078 c3/return -6079 -6080 has-metadata?: # word : (address slice), s : (address string) -> EAX : boolean -6081 # pseudocode: -6082 # var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name -6083 # curr = twig->end -6084 # while true -6085 # twig = next-token-from-slice(curr, word->end, '/') -6086 # if (twig.empty()) break -6087 # if (slice-equal?(twig, s)) return true -6088 # curr = twig->end -6089 # return false -6090 # . prolog -6091 55/push-EBP -6092 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6093 # . save registers -6094 51/push-ECX -6095 52/push-EDX -6096 56/push-ESI -6097 57/push-EDI -6098 # ESI = word -6099 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -6100 # EDX = word->end -6101 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX -6102 # var twig/EDI : (address slice) = {0, 0} -6103 68/push 0/imm32/end -6104 68/push 0/imm32/start -6105 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI -6106 # next-token-from-slice(word->start, word->end, '/', twig) -6107 # . . push args -6108 57/push-EDI -6109 68/push 0x2f/imm32/slash -6110 52/push-EDX -6111 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI -6112 # . . call -6113 e8/call next-token-from-slice/disp32 -6114 # . . discard args -6115 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -6116 # curr/ECX = twig->end -6117 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX -6118 $has-metadata?:loop: -6119 # next-token-from-slice(curr, word->end, '/', twig) -6120 # . . push args -6121 57/push-EDI -6122 68/push 0x2f/imm32/slash -6123 52/push-EDX -6124 51/push-ECX -6125 # . . call -6126 e8/call next-token-from-slice/disp32 -6127 # . . discard args -6128 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -6129 # if (slice-empty?(twig)) return false -6130 # . EAX = slice-empty?(twig) -6131 # . . push args -6132 57/push-EDI -6133 # . . call -6134 e8/call slice-empty?/disp32 -6135 # . . discard args -6136 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6137 # . if (EAX != 0) return false -6138 3d/compare-EAX-and 0/imm32 -6139 75/jump-if-not-equal $has-metadata?:false/disp8 -6140 # if (slice-equal?(twig, s)) return true -6141 # . EAX = slice-equal?(twig, s) -6142 # . . push args -6143 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -6144 57/push-EDI -6145 # . . call -6146 e8/call slice-equal?/disp32 -6147 # . . discard args -6148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6149 # . if (EAX != 0) return true -6150 3d/compare-EAX-and 0/imm32 -6151 75/jump-if-not-equal $has-metadata?:true/disp8 -6152 # curr = twig->end -6153 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX -6154 eb/jump $has-metadata?:loop/disp8 -6155 $has-metadata?:true: -6156 b8/copy-to-EAX 1/imm32/true -6157 eb/jump $has-metadata?:end/disp8 -6158 $has-metadata?:false: -6159 b8/copy-to-EAX 0/imm32/false -6160 $has-metadata?:end: -6161 # . reclaim locals -6162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6163 # . restore registers -6164 5f/pop-to-EDI -6165 5e/pop-to-ESI -6166 5a/pop-to-EDX -6167 59/pop-to-ECX -6168 # . epilog -6169 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6170 5d/pop-to-EBP -6171 c3/return -6172 -6173 test-has-metadata-true: -6174 # . prolog -6175 55/push-EBP -6176 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6177 # (EAX..ECX) = "ab/c" -6178 b8/copy-to-EAX "ab/c"/imm32 -6179 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -6180 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -6181 05/add-to-EAX 4/imm32 -6182 # var in/ESI : (address slice) = {EAX, ECX} -6183 51/push-ECX -6184 50/push-EAX -6185 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI -6186 # EAX = has-metadata?(ESI, "c") -6187 # . . push args -6188 68/push "c"/imm32 -6189 56/push-ESI -6190 # . . call -6191 e8/call has-metadata?/disp32 -6192 # . . discard args -6193 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6194 # check-ints-equal(EAX, 1, msg) -6195 # . . push args -6196 68/push "F - test-has-metadata-true"/imm32 -6197 68/push 1/imm32/true -6198 50/push-EAX -6199 # . . call -6200 e8/call check-ints-equal/disp32 -6201 # . . discard args -6202 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6203 # . epilog -6204 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6205 5d/pop-to-EBP -6206 c3/return -6207 -6208 test-has-metadata-false: -6209 # . prolog -6210 55/push-EBP -6211 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6212 # (EAX..ECX) = "ab/c" -6213 b8/copy-to-EAX "ab/c"/imm32 -6214 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -6215 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -6216 05/add-to-EAX 4/imm32 -6217 # var in/ESI : (address slice) = {EAX, ECX} -6218 51/push-ECX -6219 50/push-EAX -6220 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI -6221 # EAX = has-metadata?(ESI, "c") -6222 # . . push args -6223 68/push "d"/imm32 -6224 56/push-ESI -6225 # . . call -6226 e8/call has-metadata?/disp32 -6227 # . . discard args -6228 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6229 # check-ints-equal(EAX, 0, msg) -6230 # . . push args -6231 68/push "F - test-has-metadata-false"/imm32 -6232 68/push 0/imm32/false -6233 50/push-EAX -6234 # . . call -6235 e8/call check-ints-equal/disp32 -6236 # . . discard args -6237 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6238 # . epilog -6239 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6240 5d/pop-to-EBP -6241 c3/return -6242 -6243 test-has-metadata-ignore-name: -6244 # . prolog -6245 55/push-EBP -6246 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6247 # (EAX..ECX) = "a/b" -6248 b8/copy-to-EAX "a/b"/imm32 -6249 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -6250 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -6251 05/add-to-EAX 4/imm32 -6252 # var in/ESI : (address slice) = {EAX, ECX} -6253 51/push-ECX -6254 50/push-EAX -6255 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI -6256 # EAX = has-metadata?(ESI, "a") -6257 # . . push args -6258 68/push "a"/imm32 -6259 56/push-ESI -6260 # . . call -6261 e8/call has-metadata?/disp32 -6262 # . . discard args -6263 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6264 # check-ints-equal(EAX, 0, msg) -6265 # . . push args -6266 68/push "F - test-has-metadata-ignore-name"/imm32 -6267 68/push 0/imm32/false -6268 50/push-EAX -6269 # . . call -6270 e8/call check-ints-equal/disp32 -6271 # . . discard args -6272 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6273 # . epilog -6274 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6275 5d/pop-to-EBP -6276 c3/return -6277 -6278 test-has-metadata-multiple-true: -6279 # . prolog -6280 55/push-EBP -6281 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6282 # (EAX..ECX) = "a/b/c" -6283 b8/copy-to-EAX "a/b/c"/imm32 -6284 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -6285 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -6286 05/add-to-EAX 4/imm32 -6287 # var in/ESI : (address slice) = {EAX, ECX} -6288 51/push-ECX -6289 50/push-EAX -6290 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI -6291 # EAX = has-metadata?(ESI, "c") -6292 # . . push args -6293 68/push "c"/imm32 -6294 56/push-ESI -6295 # . . call -6296 e8/call has-metadata?/disp32 -6297 # . . discard args -6298 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6299 # check-ints-equal(EAX, 1, msg) -6300 # . . push args -6301 68/push "F - test-has-metadata-multiple-true"/imm32 -6302 68/push 1/imm32/true -6303 50/push-EAX -6304 # . . call -6305 e8/call check-ints-equal/disp32 -6306 # . . discard args -6307 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6308 # . epilog -6309 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6310 5d/pop-to-EBP -6311 c3/return -6312 -6313 test-has-metadata-multiple-false: -6314 # . prolog -6315 55/push-EBP -6316 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6317 # (EAX..ECX) = "a/b/c" -6318 b8/copy-to-EAX "a/b/c"/imm32 -6319 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -6320 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX -6321 05/add-to-EAX 4/imm32 -6322 # var in/ESI : (address slice) = {EAX, ECX} -6323 51/push-ECX -6324 50/push-EAX -6325 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI -6326 # EAX = has-metadata?(ESI, "d") -6327 # . . push args -6328 68/push "d"/imm32 -6329 56/push-ESI -6330 # . . call -6331 e8/call has-metadata?/disp32 -6332 # . . discard args -6333 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6334 # check-ints-equal(EAX, 0, msg) -6335 # . . push args -6336 68/push "F - test-has-metadata-multiple-false"/imm32 -6337 68/push 0/imm32/false -6338 50/push-EAX -6339 # . . call -6340 e8/call check-ints-equal/disp32 -6341 # . . discard args -6342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6343 # . epilog -6344 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6345 5d/pop-to-EBP -6346 c3/return -6347 -6348 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print -6349 # it in 'width' bytes of hex, least significant first. -6350 # Otherwise just print the entire word including metadata. -6351 # Always print a trailing space. -6352 emit: # out : (address buffered-file), word : (address slice), width : int -> <void> -6353 # . prolog -6354 55/push-EBP -6355 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6356 # . save registers -6357 50/push-EAX -6358 56/push-ESI -6359 57/push-EDI -6360 # ESI = word -6361 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI -6362 # var name/EDI : (address slice) = {0, 0} -6363 68/push 0/imm32/end -6364 68/push 0/imm32/start -6365 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI -6366 # datum = next-token-from-slice(word->start, word->end, '/') -6367 # . . push args -6368 57/push-EDI -6369 68/push 0x2f/imm32/slash -6370 ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) -6371 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI -6372 # . . call -6373 e8/call next-token-from-slice/disp32 -6374 # . . discard args -6375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -6376 # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return -6377 # . EAX = is-valid-name?(name) -6378 # . . push args -6379 57/push-EDI -6380 # . . call -6381 e8/call is-valid-name?/disp32 -6382 # . . discard args -6383 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6384 # . if (EAX != 0) -6385 3d/compare-EAX-and 0/imm32 -6386 74/jump-if-equal $emit:hex-int/disp8 -6387 $emit:name: -6388 # . write-slice-buffered(out, word) -6389 # . . push args -6390 56/push-ESI -6391 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -6392 # . . call -6393 e8/call write-slice-buffered/disp32 -6394 # . . discard args -6395 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6396 # . write-buffered(out, " ") -6397 # . . push args -6398 68/push " "/imm32 -6399 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -6400 # . . call -6401 e8/call write-buffered/disp32 -6402 # . . discard args -6403 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6404 # . return -6405 eb/jump $emit:end/disp8 -6406 # otherwise emit-hex(out, parse-hex-int(datum), width) -6407 # (Weird shit can happen here if the datum of 'word' isn't either a valid -6408 # name or a hex number, but we're only going to be passing in real legal -6409 # programs. We just want to make sure that valid names aren't treated as -6410 # (valid) hex numbers.) -6411 $emit:hex-int: -6412 # . value/EAX = parse-hex-int(datum) -6413 # . . push args -6414 57/push-EDI -6415 # . . call -6416 e8/call parse-hex-int/disp32 -6417 # . . discard args -6418 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6419 # . emit-hex(out, value, width) -6420 # . . push args -6421 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -6422 50/push-EAX -6423 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -6424 # . . call -6425 e8/call emit-hex/disp32 -6426 # . . discard args -6427 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6428 $emit:end: -6429 # . reclaim locals -6430 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -6431 # . restore registers -6432 5f/pop-to-EDI -6433 5e/pop-to-ESI -6434 58/pop-to-EAX -6435 # . epilog -6436 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6437 5d/pop-to-EBP -6438 c3/return -6439 -6440 test-emit-number: -6441 # . prolog -6442 55/push-EBP -6443 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6444 # setup -6445 # . clear-stream(_test-output-stream) -6446 # . . push args -6447 68/push _test-output-stream/imm32 -6448 # . . call -6449 e8/call clear-stream/disp32 -6450 # . . discard args -6451 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6452 # . clear-stream(_test-output-buffered-file+4) -6453 # . . push args -6454 b8/copy-to-EAX _test-output-buffered-file/imm32 -6455 05/add-to-EAX 4/imm32 -6456 50/push-EAX -6457 # . . call -6458 e8/call clear-stream/disp32 -6459 # . . discard args -6460 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6461 # var slice/ECX = "30" -6462 68/push _test-slice-three-zero-end/imm32/end -6463 68/push _test-slice-three-zero/imm32/start -6464 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6465 # emit(_test-output-buffered-file, slice, 1) -6466 # . . push args -6467 68/push 1/imm32 -6468 51/push-ECX -6469 68/push _test-output-buffered-file/imm32 -6470 # . . call -6471 e8/call emit/disp32 -6472 # . . discard args -6473 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6474 # flush(_test-output-buffered-file) -6475 # . . push args -6476 68/push _test-output-buffered-file/imm32 -6477 # . . call -6478 e8/call flush/disp32 -6479 # . . discard args -6480 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6481 # check-stream-equal(_test-output-stream, "30 ", msg) -6482 # . . push args -6483 68/push "F - test-emit-number/1"/imm32 -6484 68/push "30 "/imm32 -6485 68/push _test-output-stream/imm32 -6486 # . . call -6487 e8/call check-stream-equal/disp32 -6488 # . . discard args -6489 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6490 # . epilog -6491 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6492 5d/pop-to-EBP -6493 c3/return -6494 -6495 test-emit-negative-number: -6496 # test support for sign-extending negative numbers -6497 # . prolog -6498 55/push-EBP -6499 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6500 # setup -6501 # . clear-stream(_test-output-stream) -6502 # . . push args -6503 68/push _test-output-stream/imm32 -6504 # . . call -6505 e8/call clear-stream/disp32 -6506 # . . discard args -6507 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6508 # . clear-stream(_test-output-buffered-file+4) -6509 # . . push args -6510 b8/copy-to-EAX _test-output-buffered-file/imm32 -6511 05/add-to-EAX 4/imm32 -6512 50/push-EAX -6513 # . . call -6514 e8/call clear-stream/disp32 -6515 # . . discard args -6516 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6517 # var slice/ECX = "-2" -6518 68/push _test-slice-negative-two-end/imm32/end -6519 68/push _test-slice-negative-two/imm32/start -6520 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6521 # emit(_test-output-buffered-file, slice, 2) -6522 # . . push args -6523 68/push 2/imm32 -6524 51/push-ECX -6525 68/push _test-output-buffered-file/imm32 -6526 # . . call -6527 e8/call emit/disp32 -6528 # . . discard args -6529 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6530 # flush(_test-output-buffered-file) -6531 # . . push args -6532 68/push _test-output-buffered-file/imm32 -6533 # . . call -6534 e8/call flush/disp32 -6535 # . . discard args -6536 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6537 # check-stream-equal(_test-output-stream, "fe ff ", msg) -6538 # . . push args -6539 68/push "F - test-emit-number/1"/imm32 -6540 68/push "fe ff "/imm32 -6541 68/push _test-output-stream/imm32 -6542 # . . call -6543 e8/call check-stream-equal/disp32 -6544 # . . discard args -6545 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6546 # . epilog -6547 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6548 5d/pop-to-EBP -6549 c3/return -6550 -6551 test-emit-number-with-metadata: -6552 # . prolog -6553 55/push-EBP -6554 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6555 # setup -6556 # . clear-stream(_test-output-stream) -6557 # . . push args -6558 68/push _test-output-stream/imm32 -6559 # . . call -6560 e8/call clear-stream/disp32 -6561 # . . discard args -6562 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6563 # . clear-stream(_test-output-buffered-file+4) -6564 # . . push args -6565 b8/copy-to-EAX _test-output-buffered-file/imm32 -6566 05/add-to-EAX 4/imm32 -6567 50/push-EAX -6568 # . . call -6569 e8/call clear-stream/disp32 -6570 # . . discard args -6571 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6572 # var slice/ECX = "-2/foo" -6573 68/push _test-slice-negative-two-metadata-end/imm32/end -6574 68/push _test-slice-negative-two/imm32/start -6575 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6576 # emit(_test-output-buffered-file, slice, 2) -6577 # . . push args -6578 68/push 2/imm32 -6579 51/push-ECX -6580 68/push _test-output-buffered-file/imm32 -6581 # . . call -6582 e8/call emit/disp32 -6583 # . . discard args -6584 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6585 # flush(_test-output-buffered-file) -6586 # . . push args -6587 68/push _test-output-buffered-file/imm32 -6588 # . . call -6589 e8/call flush/disp32 -6590 # . . discard args -6591 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6592 # the '/foo' will have no impact on the output -6593 # check-stream-equal(_test-output-stream, "fe ff ", msg) -6594 # . . push args -6595 68/push "F - test-emit-number-with-metadata"/imm32 -6596 68/push "fe ff "/imm32 -6597 68/push _test-output-stream/imm32 -6598 # . . call -6599 e8/call check-stream-equal/disp32 -6600 # . . discard args -6601 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6602 # . epilog -6603 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6604 5d/pop-to-EBP -6605 c3/return -6606 -6607 test-emit-non-number: -6608 # . prolog -6609 55/push-EBP -6610 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6611 # setup -6612 # . clear-stream(_test-output-stream) -6613 # . . push args -6614 68/push _test-output-stream/imm32 -6615 # . . call -6616 e8/call clear-stream/disp32 -6617 # . . discard args -6618 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6619 # . clear-stream(_test-output-buffered-file+4) -6620 # . . push args -6621 b8/copy-to-EAX _test-output-buffered-file/imm32 -6622 05/add-to-EAX 4/imm32 -6623 50/push-EAX -6624 # . . call -6625 e8/call clear-stream/disp32 -6626 # . . discard args -6627 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6628 # var slice/ECX = "xyz" -6629 68/push _test-slice-non-number-word-end/imm32/end -6630 68/push _test-slice-non-number-word/imm32/start -6631 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6632 # emit(_test-output-buffered-file, slice, 2) -6633 # . . push args -6634 68/push 2/imm32 -6635 51/push-ECX -6636 68/push _test-output-buffered-file/imm32 -6637 # . . call -6638 e8/call emit/disp32 -6639 # . . discard args -6640 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6641 # flush(_test-output-buffered-file) -6642 # . . push args -6643 68/push _test-output-buffered-file/imm32 -6644 # . . call -6645 e8/call flush/disp32 -6646 # . . discard args -6647 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6648 # check-stream-equal(_test-output-stream, "xyz", msg) -6649 # . . push args -6650 68/push "F - test-emit-non-number"/imm32 -6651 68/push "xyz "/imm32 -6652 68/push _test-output-stream/imm32 -6653 # . . call -6654 e8/call check-stream-equal/disp32 -6655 # . . discard args -6656 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6657 # . epilog -6658 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6659 5d/pop-to-EBP -6660 c3/return -6661 -6662 test-emit-non-number-with-metadata: -6663 # . prolog -6664 55/push-EBP -6665 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6666 # setup -6667 # . clear-stream(_test-output-stream) -6668 # . . push args -6669 68/push _test-output-stream/imm32 -6670 # . . call -6671 e8/call clear-stream/disp32 -6672 # . . discard args -6673 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6674 # . clear-stream(_test-output-buffered-file+4) -6675 # . . push args -6676 b8/copy-to-EAX _test-output-buffered-file/imm32 -6677 05/add-to-EAX 4/imm32 -6678 50/push-EAX -6679 # . . call -6680 e8/call clear-stream/disp32 -6681 # . . discard args -6682 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6683 # var slice/ECX = "xyz/" -6684 68/push _test-slice-non-number-word-metadata-end/imm32/end -6685 68/push _test-slice-non-number-word/imm32/start -6686 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6687 # emit(_test-output-buffered-file, slice, 2) -6688 # . . push args -6689 68/push 2/imm32 -6690 51/push-ECX -6691 68/push _test-output-buffered-file/imm32 -6692 # . . call -6693 e8/call emit/disp32 -6694 # . . discard args -6695 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6696 # flush(_test-output-buffered-file) -6697 # . . push args -6698 68/push _test-output-buffered-file/imm32 -6699 # . . call -6700 e8/call flush/disp32 -6701 # . . discard args -6702 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6703 # check-stream-equal(_test-output-stream, "xyz/", msg) -6704 # . . push args -6705 68/push "F - test-emit-non-number-with-metadata"/imm32 -6706 68/push "xyz/ "/imm32 -6707 68/push _test-output-stream/imm32 -6708 # . . call -6709 e8/call check-stream-equal/disp32 -6710 # . . discard args -6711 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6712 # . epilog -6713 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6714 5d/pop-to-EBP -6715 c3/return -6716 -6717 test-emit-non-number-with-all-hex-digits-and-metadata: -6718 # . prolog -6719 55/push-EBP -6720 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6721 # setup -6722 # . clear-stream(_test-output-stream) -6723 # . . push args -6724 68/push _test-output-stream/imm32 -6725 # . . call -6726 e8/call clear-stream/disp32 -6727 # . . discard args -6728 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6729 # . clear-stream(_test-output-buffered-file+4) -6730 # . . push args -6731 b8/copy-to-EAX _test-output-buffered-file/imm32 -6732 05/add-to-EAX 4/imm32 -6733 50/push-EAX -6734 # . . call -6735 e8/call clear-stream/disp32 -6736 # . . discard args -6737 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6738 # var slice/ECX = "abcd/xyz" -6739 68/push _test-slice-hexlike-non-number-word-metadata-end/imm32/end -6740 68/push _test-slice-hexlike-non-number-word/imm32/start -6741 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6742 # emit(_test-output-buffered-file, slice, 2) -6743 # . . push args -6744 68/push 2/imm32 -6745 51/push-ECX -6746 68/push _test-output-buffered-file/imm32 -6747 # . . call -6748 e8/call emit/disp32 -6749 # . . discard args -6750 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6751 # flush(_test-output-buffered-file) -6752 # . . push args -6753 68/push _test-output-buffered-file/imm32 -6754 # . . call -6755 e8/call flush/disp32 -6756 # . . discard args -6757 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6758 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -6784 # check-stream-equal(_test-output-stream, "abcd/xyz") -6785 # . . push args -6786 68/push "F - test-emit-non-number-with-all-hex-digits"/imm32 -6787 68/push "abcd/xyz "/imm32 -6788 68/push _test-output-stream/imm32 -6789 # . . call -6790 e8/call check-stream-equal/disp32 -6791 # . . discard args -6792 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6793 # . epilog -6794 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6795 5d/pop-to-EBP -6796 c3/return -6797 -6798 # conditions for 'valid' names that are not at risk of looking like hex numbers -6799 # keep in sync with the rules in labels.cc -6800 #: - if it starts with a digit, it's treated as a number. If it can't be -6801 #: parsed as hex it will raise an error. -6802 #: - if it starts with '-' it's treated as a number. -6803 #: - if it starts with '0x' it's treated as a number. (redundant) -6804 #: - if it's two characters long, it can't be a name. Either it's a hex -6805 #: byte, or it raises an error. -6806 is-valid-name?: # in : (address slice) -> EAX : boolean -6807 # . prolog -6808 55/push-EBP -6809 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6810 # . save registers -6811 51/push-ECX -6812 56/push-ESI -6813 # ESI = in -6814 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -6815 # start/ECX = in->start -6816 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX -6817 # end/EAX = in->end -6818 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX -6819 $is-valid-name?:check0: -6820 # if (start >= end) return false -6821 39/compare 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # compare ECX with EAX -6822 7d/jump-if-greater-or-equal $is-valid-name?:false/disp8 -6823 $is-valid-name?:check1: -6824 # EAX -= ECX -6825 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX -6826 # if (EAX == 2) return false -6827 3d/compare-EAX-and 2/imm32 -6828 74/jump-if-equal $is-valid-name?:false/disp8 -6829 $is-valid-name?:check2: -6830 # c/EAX = *ECX -6831 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -6832 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -6833 # if (c == "-") return false -6834 3d/compare-EAX-and 2d/imm32/- -6835 74/jump-if-equal $is-valid-name?:false/disp8 -6836 $is-valid-name?:check3a: -6837 # if (c < "0") return true -6838 3d/compare-EAX-with 30/imm32/0 -6839 7c/jump-if-lesser $is-valid-name?:true/disp8 -6840 $is-valid-name?:check3b: -6841 # if (c > "9") return true -6842 3d/compare-EAX-with 39/imm32/9 -6843 7f/jump-if-greater $is-valid-name?:true/disp8 -6844 $is-valid-name?:false: -6845 # return false -6846 b8/copy-to-EAX 0/imm32/false -6847 eb/jump $is-valid-name?:end/disp8 -6848 $is-valid-name?:true: -6849 # return true -6850 b8/copy-to-EAX 1/imm32/true -6851 $is-valid-name?:end: -6852 # . restore registers -6853 5e/pop-to-ESI -6854 59/pop-to-ECX -6855 # . epilog -6856 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6857 5d/pop-to-EBP -6858 c3/return -6859 -6860 test-is-valid-name-digit-prefix: -6861 # . prolog -6862 55/push-EBP -6863 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6864 # var slice/ECX = "34" -6865 68/push _test-slice-hex-int-end/imm32 -6866 68/push _test-slice-hex-int/imm32 -6867 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6868 # EAX = is-valid-name?(slice) -6869 # . . push args -6870 51/push-ECX -6871 # . . call -6872 e8/call is-valid-name?/disp32 -6873 # . . discard args -6874 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6875 # check-ints-equal(EAX, 0, msg) -6876 # . . push args -6877 68/push "F - test-is-valid-name-digit-prefix"/imm32 -6878 68/push 0/imm32/false -6879 50/push-EAX -6880 # . . call -6881 e8/call check-ints-equal/disp32 -6882 # . . discard args -6883 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6884 # . epilog -6885 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6886 5d/pop-to-EBP -6887 c3/return -6888 -6889 test-is-valid-name-negative-prefix: -6890 # . prolog -6891 55/push-EBP -6892 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6893 # var slice/ECX = "-0x34" -6894 68/push _test-slice-hex-int-with-0x-prefix-end/imm32 -6895 68/push _test-slice-hex-int-with-0x-prefix-negative/imm32 -6896 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6897 # EAX = is-valid-name?(slice) -6898 # . . push args -6899 51/push-ECX -6900 # . . call -6901 e8/call is-valid-name?/disp32 -6902 # . . discard args -6903 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6904 # check-ints-equal(EAX, 0, msg) -6905 # . . push args -6906 68/push "F - test-is-valid-name-negative-prefix"/imm32 -6907 68/push 0/imm32/false -6908 50/push-EAX -6909 # . . call -6910 e8/call check-ints-equal/disp32 -6911 # . . discard args -6912 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6913 # . epilog -6914 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6915 5d/pop-to-EBP -6916 c3/return -6917 -6918 test-is-valid-name-0x-prefix: -6919 # . prolog -6920 55/push-EBP -6921 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6922 # var slice/ECX = "0x34" -6923 68/push _test-slice-hex-int-with-0x-prefix-end/imm32 -6924 68/push _test-slice-hex-int-with-0x-prefix/imm32 -6925 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6926 # EAX = is-valid-name?(slice) -6927 # . . push args -6928 51/push-ECX -6929 # . . call -6930 e8/call is-valid-name?/disp32 -6931 # . . discard args -6932 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6933 # check-ints-equal(EAX, 0, msg) -6934 # . . push args -6935 68/push "F - test-is-valid-name-0x-prefix"/imm32 -6936 68/push 0/imm32/false -6937 50/push-EAX -6938 # . . call -6939 e8/call check-ints-equal/disp32 -6940 # . . discard args -6941 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6942 # . epilog -6943 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6944 5d/pop-to-EBP -6945 c3/return -6946 -6947 test-is-valid-name-starts-with-pre-digit: -6948 # . prolog -6949 55/push-EBP -6950 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6951 # var slice/ECX = "/03" -6952 68/push _test-slice-with-slash-prefix-end/imm32 -6953 68/push _test-slice-with-slash-prefix/imm32 -6954 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6955 # EAX = is-valid-name?(slice) -6956 # . . push args -6957 51/push-ECX -6958 # . . call -6959 e8/call is-valid-name?/disp32 -6960 # . . discard args -6961 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6962 # check-ints-equal(EAX, 1, msg) -6963 # . . push args -6964 68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32 -6965 68/push 1/imm32/true -6966 50/push-EAX -6967 # . . call -6968 e8/call check-ints-equal/disp32 -6969 # . . discard args -6970 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -6971 # . epilog -6972 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -6973 5d/pop-to-EBP -6974 c3/return -6975 -6976 test-is-valid-name-starts-with-post-digit: -6977 # . prolog -6978 55/push-EBP -6979 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -6980 # var slice/ECX = "q34" -6981 68/push _test-slice-char-and-digits-end/imm32 -6982 68/push _test-slice-char-and-digits/imm32 -6983 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -6984 # EAX = is-valid-name?(slice) -6985 # . . push args -6986 51/push-ECX -6987 # . . call -6988 e8/call is-valid-name?/disp32 -6989 # . . discard args -6990 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -6991 # check-ints-equal(EAX, 1, msg) -6992 # . . push args -6993 68/push "F - test-is-valid-name-starts-with-post-digit"/imm32 -6994 68/push 1/imm32/true -6995 50/push-EAX -6996 # . . call -6997 e8/call check-ints-equal/disp32 -6998 # . . discard args -6999 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7000 # . epilog -7001 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -7002 5d/pop-to-EBP -7003 c3/return -7004 -7005 test-is-valid-name-starts-with-digit: -7006 # . prolog -7007 55/push-EBP -7008 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -7009 # var slice/ECX = "0x34" -7010 68/push _test-slice-hex-int-with-0x-prefix-end/imm32 -7011 68/push _test-slice-hex-int-with-0x-prefix/imm32 -7012 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -7013 # EAX = is-valid-name?(slice) -7014 # . . push args -7015 51/push-ECX -7016 # . . call -7017 e8/call is-valid-name?/disp32 -7018 # . . discard args -7019 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7020 # check-ints-equal(EAX, 0, msg) -7021 # . . push args -7022 68/push "F - test-is-valid-name-starts-with-digit"/imm32 -7023 68/push 0/imm32/false -7024 50/push-EAX -7025 # . . call -7026 e8/call check-ints-equal/disp32 -7027 # . . discard args -7028 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7029 # . epilog -7030 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -7031 5d/pop-to-EBP -7032 c3/return -7033 -7034 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte -7035 emit-hex: # out : (address buffered-file), n : int, width : int -> <void> -7036 # . prolog -7037 55/push-EBP -7038 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -7039 # . save registers -7040 50/push-EAX -7041 51/push-ECX -7042 52/push-EDX -7043 53/push-EBX -7044 57/push-EDI -7045 # EDI = out -7046 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI -7047 # EBX = n -7048 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX -7049 # EDX = width -7050 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX -7051 # var curr/ECX = 0 -7052 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -7053 $emit-hex:loop: -7054 # if (curr >= width) break -7055 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -7056 7d/jump-if-greater-or-equal $emit-hex:end/disp8 -7057 # print-byte-buffered(out, EBX) -7058 # . . push args -7059 53/push-EBX -7060 57/push-EDI -7061 # . . call -7062 e8/call print-byte-buffered/disp32 -7063 # . . discard args -7064 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -7065 # write-byte-buffered(out, ' ') -7066 # . . push args -7067 68/push 0x20/imm32/space -7068 57/push-EDI -7069 # . . call -7070 e8/call write-byte-buffered/disp32 -7071 # . . discard args -7072 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -7073 # EBX = EBX >> 8 -7074 c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/EBX . . . . . 8/imm8 # shift EBX right by 8 bits, while padding zeroes -7075 $emit-hex:continue: -7076 # ++curr -7077 41/increment-ECX -7078 eb/jump $emit-hex:loop/disp8 -7079 $emit-hex:end: -7080 # . restore registers -7081 5f/pop-to-EDI -7082 5b/pop-to-EBX -7083 5a/pop-to-EDX -7084 59/pop-to-ECX -7085 58/pop-to-EAX -7086 # . epilog -7087 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -7088 5d/pop-to-EBP -7089 c3/return -7090 -7091 test-emit-hex-single-byte: -7092 # setup -7093 # . clear-stream(_test-output-stream) -7094 # . . push args -7095 68/push _test-output-stream/imm32 -7096 # . . call -7097 e8/call clear-stream/disp32 -7098 # . . discard args -7099 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7100 # . clear-stream(_test-output-buffered-file+4) -7101 # . . push args -7102 b8/copy-to-EAX _test-output-buffered-file/imm32 -7103 05/add-to-EAX 4/imm32 -7104 50/push-EAX -7105 # . . call -7106 e8/call clear-stream/disp32 -7107 # . . discard args -7108 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7109 # emit-hex(_test-output-buffered-file, 0xab, 1) -7110 # . . push args -7111 68/push 1/imm32 -7112 68/push 0xab/imm32 -7113 68/push _test-output-buffered-file/imm32 -7114 # . . call -7115 e8/call emit-hex/disp32 -7116 # . . discard args -7117 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7118 # flush(_test-output-buffered-file) -7119 # . . push args -7120 68/push _test-output-buffered-file/imm32 -7121 # . . call -7122 e8/call flush/disp32 -7123 # . . discard args -7124 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7125 # check-ints-equal(*_test-output-stream->data, 'ab ', msg) -7126 # . . push args -7127 68/push "F - test-emit-hex-single-byte"/imm32 -7128 68/push 0x206261/imm32 -7129 # . . push *_test-output-stream->data -7130 b8/copy-to-EAX _test-output-stream/imm32 -7131 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -7132 # . . call -7133 e8/call check-ints-equal/disp32 -7134 # . . discard args -7135 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7136 # . end -7137 c3/return -7138 -7139 test-emit-hex-multiple-byte: -7140 # setup -7141 # . clear-stream(_test-output-stream) -7142 # . . push args -7143 68/push _test-output-stream/imm32 -7144 # . . call -7145 e8/call clear-stream/disp32 -7146 # . . discard args -7147 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7148 # . clear-stream(_test-output-buffered-file+4) -7149 # . . push args -7150 b8/copy-to-EAX _test-output-buffered-file/imm32 -7151 05/add-to-EAX 4/imm32 -7152 50/push-EAX -7153 # . . call -7154 e8/call clear-stream/disp32 -7155 # . . discard args -7156 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7157 # emit-hex(_test-output-buffered-file, 0x1234, 2) -7158 # . . push args -7159 68/push 2/imm32 -7160 68/push 0x1234/imm32 -7161 68/push _test-output-buffered-file/imm32 -7162 # . . call -7163 e8/call emit-hex/disp32 -7164 # . . discard args -7165 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7166 # flush(_test-output-buffered-file) -7167 # . . push args -7168 68/push _test-output-buffered-file/imm32 -7169 # . . call -7170 e8/call flush/disp32 -7171 # . . discard args -7172 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7173 # check-stream-equal(_test-output-stream, "34 12 ", msg) -7174 # . . push args -7175 68/push "F - test-emit-hex-multiple-byte/1"/imm32 -7176 68/push "34 12 "/imm32 -7177 68/push _test-output-stream/imm32 -7178 # . . call -7179 e8/call check-stream-equal/disp32 -7180 # . . discard args -7181 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7182 # . end -7183 c3/return -7184 -7185 test-emit-hex-zero-pad: -7186 # setup -7187 # . clear-stream(_test-output-stream) -7188 # . . push args -7189 68/push _test-output-stream/imm32 -7190 # . . call -7191 e8/call clear-stream/disp32 -7192 # . . discard args -7193 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7194 # . clear-stream(_test-output-buffered-file+4) -7195 # . . push args -7196 b8/copy-to-EAX _test-output-buffered-file/imm32 -7197 05/add-to-EAX 4/imm32 -7198 50/push-EAX -7199 # . . call -7200 e8/call clear-stream/disp32 -7201 # . . discard args -7202 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7203 # emit-hex(_test-output-buffered-file, 0xab, 2) -7204 # . . push args -7205 68/push 2/imm32 -7206 68/push 0xab/imm32 -7207 68/push _test-output-buffered-file/imm32 -7208 # . . call -7209 e8/call emit-hex/disp32 -7210 # . . discard args -7211 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7212 # flush(_test-output-buffered-file) -7213 # . . push args -7214 68/push _test-output-buffered-file/imm32 -7215 # . . call -7216 e8/call flush/disp32 -7217 # . . discard args -7218 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7219 # check(_test-output-stream->data == 'ab 00 ') -7220 # . . push args -7221 68/push "F - test-emit-hex-zero-pad/1"/imm32 -7222 68/push "ab 00 "/imm32 -7223 68/push _test-output-stream/imm32 -7224 # . . call -7225 e8/call check-stream-equal/disp32 -7226 # . . discard args -7227 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7228 # . end -7229 c3/return -7230 -7231 test-emit-hex-negative: -7232 # setup -7233 # . clear-stream(_test-output-stream) -7234 # . . push args -7235 68/push _test-output-stream/imm32 -7236 # . . call -7237 e8/call clear-stream/disp32 -7238 # . . discard args -7239 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7240 # . clear-stream(_test-output-buffered-file+4) -7241 # . . push args -7242 b8/copy-to-EAX _test-output-buffered-file/imm32 -7243 05/add-to-EAX 4/imm32 -7244 50/push-EAX -7245 # . . call -7246 e8/call clear-stream/disp32 -7247 # . . discard args -7248 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7249 # emit-hex(_test-output-buffered-file, -1, 2) -7250 # . . push args -7251 68/push 2/imm32 -7252 68/push -1/imm32 -7253 68/push _test-output-buffered-file/imm32 -7254 # . . call -7255 e8/call emit-hex/disp32 -7256 # . . discard args -7257 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7258 # flush(_test-output-buffered-file) -7259 # . . push args -7260 68/push _test-output-buffered-file/imm32 -7261 # . . call -7262 e8/call flush/disp32 -7263 # . . discard args -7264 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7265 # check-stream-equal(_test-output-stream == "ff ff ") -7266 # . . push args -7267 68/push "F - test-emit-hex-negative/1"/imm32 -7268 68/push "ff ff "/imm32 -7269 68/push _test-output-stream/imm32 -7270 # . . call -7271 e8/call check-stream-equal/disp32 -7272 # . . discard args -7273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -7274 # . end -7275 c3/return -7276 -7277 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/')) -7278 parse-datum-of-word: # word : (address slice) -> value/EAX -7279 # . prolog -7280 55/push-EBP -7281 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -7282 # . save registers -7283 51/push-ECX -7284 56/push-ESI -7285 # ESI = word -7286 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -7287 # var slice/ECX = {0, 0} -7288 68/push 0/imm32/end -7289 68/push 0/imm32/start -7290 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -7291 # slice = next-token-from-slice(word->start, word->end, '/') -7292 # . . push args -7293 51/push-ECX -7294 68/push 0x2f/imm32/slash -7295 ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) -7296 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI -7297 # . . call -7298 e8/call next-token-from-slice/disp32 -7299 # . . discard args -7300 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -7301 # value/EAX = parse-hex-int(slice) -7302 # . . push args -7303 51/push-ECX -7304 # . . call -7305 e8/call parse-hex-int/disp32 -7306 # . . discard args -7307 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -7308 $parse-datum-of-word:end: -7309 # . reclaim locals -7310 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -7311 # . restore registers -7312 5e/pop-to-ESI -7313 59/pop-to-ECX -7314 # . epilog -7315 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -7316 5d/pop-to-EBP -7317 c3/return -7318 -7319 == data -7320 -7321 _test-slice-negative-two: -7322 2d/- 32/2 -7323 _test-slice-negative-two-end: -7324 2f/slash 66/f 6f/o 6f/o -7325 _test-slice-negative-two-metadata-end: -7326 -7327 _test-slice-three-zero: -7328 33/3 30/0 -7329 _test-slice-three-zero-end: -7330 -7331 _test-slice-non-number-word: -7332 78/x 79/y 7a/z -7333 _test-slice-non-number-word-end: -7334 2f/slash -7335 _test-slice-non-number-word-metadata-end: -7336 -7337 _test-slice-hexlike-non-number-word: -7338 61/a 62/b 63/c 64/d -7339 _test-slice-hexlike-non-number-word-end: -7340 2f/slash -7341 78/x 79/y 7a/z -7342 _test-slice-hexlike-non-number-word-metadata-end: -7343 -7344 _test-slice-with-slash-prefix: -7345 2f/slash 30/0 33/3 -7346 _test-slice-with-slash-prefix-end: -7347 -7348 # . . vim:nowrap:textwidth=0 +5851 51/push-ECX +5852 68/push 0x2f/imm32/slash +5853 ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) +5854 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI +5855 # . . call +5856 e8/call next-token-from-slice/disp32 +5857 # . . discard args +5858 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +5859 # value/EAX = parse-hex-int(slice) +5860 # . . push args +5861 51/push-ECX +5862 # . . call +5863 e8/call parse-hex-int/disp32 +5864 # . . discard args +5865 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +5866 $parse-datum-of-word:end: +5867 # . reclaim locals +5868 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +5869 # . restore registers +5870 5e/pop-to-ESI +5871 59/pop-to-ECX +5872 # . epilog +5873 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +5874 5d/pop-to-EBP +5875 c3/return +5876 +5877 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/subx-common.subx.html b/html/subx/apps/subx-common.subx.html index b4f86fdf..b9d7b04d 100644 --- a/html/subx/apps/subx-common.subx.html +++ b/html/subx/apps/subx-common.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/subx-common.subx - - + + @@ -14,15 +14,18 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.subxMinorFunction { color: #875f5f; } -.LineNr { } .subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.LineNr { } +.SpecialChar { color: #d70000; } .subxTest { color: #5f8700; } -.Constant { color: #008787; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } +.Folded { color: #080808; background-color: #949494; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -39,7 +42,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -58,204 +61,3032 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/apps/subx-common.subx
-  1 # some common helpers shared by phases of the SubX converter
-  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 # write an entire stream's contents to a buffered-file
-  9 # ways to do this:
- 10 #   - construct a 'maximal slice' and pass it to write-slice-buffered
- 11 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
- 12 # we'll go with the first way for now
- 13 write-stream-data:  # f : (address buffered-file), s : (address stream) -> <void>
- 14     # . prolog
- 15     55/push-EBP
- 16     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 17     # . save registers
- 18     50/push-EAX
- 19     51/push-ECX
- 20     56/push-ESI
- 21     # ESI = s
- 22     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
- 23     # var slice/ECX = {s->data, s->data + s->write}
- 24     # . push s->data + s->write
- 25     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
- 26     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
- 27     50/push-EAX
- 28     # . push s->data
- 29     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
- 30     50/push-EAX
- 31     # . ECX = ESP
- 32     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 33     # write-slice-buffered(f, slice)
- 34     # . . push args
- 35     51/push-ECX
- 36     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 37     # . . call
- 38     e8/call  write-slice-buffered/disp32
- 39     # . . discard args
- 40     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 41 $write-stream-data:end:
- 42     # . restore locals
- 43     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 44     # . restore registers
- 45     5e/pop-to-ESI
- 46     59/pop-to-ECX
- 47     58/pop-to-EAX
- 48     # . epilog
- 49     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 50     5d/pop-to-EBP
- 51     c3/return
- 52 
- 53 test-write-stream-data:
- 54     # . prolog
- 55     55/push-EBP
- 56     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 57     # setup
- 58     # . clear-stream(_test-output-stream)
- 59     # . . push args
- 60     68/push  _test-output-stream/imm32
- 61     # . . call
- 62     e8/call  clear-stream/disp32
- 63     # . . discard args
- 64     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 65     # . clear-stream(_test-output-buffered-file+4)
- 66     # . . push args
- 67     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 68     05/add-to-EAX  4/imm32
- 69     50/push-EAX
- 70     # . . call
- 71     e8/call  clear-stream/disp32
- 72     # . . discard args
- 73     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 74     # . clear-stream(_test-input-stream)
- 75     # . . push args
- 76     68/push  _test-input-stream/imm32
- 77     # . . call
- 78     e8/call  clear-stream/disp32
- 79     # . . discard args
- 80     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 81     # initialize input
- 82     # . write(_test-input-stream, "abcd")
- 83     # . . push args
- 84     68/push  "abcd"/imm32
- 85     68/push  _test-input-stream/imm32
- 86     # . . call
- 87     e8/call  write/disp32
- 88     # . . discard args
- 89     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 90     # write-stream-data(_test-output-buffered-file, _test-input-stream)
- 91     # . . push args
- 92     68/push  _test-input-stream/imm32
- 93     68/push  _test-output-buffered-file/imm32
- 94     # . . call
- 95     e8/call  write-stream-data/disp32
- 96     # . . discard args
- 97     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 98     # check that the write happened as expected
- 99     # . flush(_test-output-buffered-file)
-100     # . . push args
-101     68/push  _test-output-buffered-file/imm32
-102     # . . call
-103     e8/call  flush/disp32
-104     # . . discard args
-105     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-106     # . check-stream-equal(_test-output-stream, "abcd", msg)
-107     # . . push args
-108     68/push  "F - test-write-stream-data"/imm32
-109     68/push  "abcd"/imm32
-110     68/push  _test-output-stream/imm32
-111     # . . call
-112     e8/call  check-stream-equal/disp32
-113     # . . discard args
-114     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-115     # . epilog
-116     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-117     5d/pop-to-EBP
-118     c3/return
-119 
-120 == data
-121 
-122 _test-input-stream:
-123     # current write index
-124     0/imm32
-125     # current read index
-126     0/imm32
-127     # length
-128     0x100/imm32  # 256 bytes
-129     # data (16 lines x 16 bytes/line)
-130     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-131     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-132     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-133     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-134     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-135     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-136     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-137     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-138     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-139     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-140     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-141     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-142     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-143     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-144     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-145     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-146 
-147 # a test buffered file for _test-input-stream
-148 _test-input-buffered-file:
-149     # file descriptor or (address stream)
-150     _test-input-stream/imm32
-151     # current write index
-152     0/imm32
-153     # current read index
-154     0/imm32
-155     # length
-156     6/imm32
-157     # data
-158     00 00 00 00 00 00  # 6 bytes
-159 
-160 _test-output-stream:
-161     # current write index
-162     0/imm32
-163     # current read index
-164     0/imm32
-165     # length
-166     0x100/imm32  # 256 bytes
-167     # data (16 lines x 16 bytes/line)
-168     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-169     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-170     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-171     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-172     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-173     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-174     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-175     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-176     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-177     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-178     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-179     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-180     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-181     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-182     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-183     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-184 
-185 # a test buffered file for _test-output-stream
-186 _test-output-buffered-file:
-187     # file descriptor or (address stream)
-188     _test-output-stream/imm32
-189     # current write index
-190     0/imm32
-191     # current read index
-192     0/imm32
-193     # length
-194     6/imm32
-195     # data
-196     00 00 00 00 00 00  # 6 bytes
-197 
-198 # . . vim:nowrap:textwidth=0
+   1 # some common helpers shared by phases of the SubX converter
+   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 # - managing tables
+   9 # SubX has rudimentary support for tables.
+  10 #
+  11 # Each table is a stream of rows.
+  12 #
+  13 # Each row consists of a 4-byte row (address to a string) and a variable-size
+  14 # value.
+  15 #
+  16 # Accessing the table performs a linear scan for a key string, and always
+  17 # requires passing in the row size.
+  18 #
+  19 # Table primitives:
+  20 #   get(stream, string, row-size)
+  21 #     aborts if not found
+  22 #   get-or-insert(stream, string, row-size)
+  23 #     inserts if not found
+  24 #   get-slice(stream, slice, row-size)
+  25 #     aborts if not found
+  26 #   leaky-get-or-insert-slice(stream, slice, row-size)
+  27 #     inserts if not found
+  28 
+  29 # 'table' is a stream of (key, value) rows
+  30 # keys are always strings (addresses; size 4 bytes)
+  31 # values may be any type, but rows (key+value) always occupy 'row-size' bytes
+  32 # scan 'table' for a row with a key 'key' and return the address of the corresponding value
+  33 # if no row is found, abort
+  34 get:  # table : (address stream {string, _}), key : (address string), row-size : int -> EAX : (address _)
+  35     # pseudocode:
+  36     #   curr = table->data
+  37     #   max = &table->data[table->write]
+  38     #   while curr < max
+  39     #     if string-equal?(key, *curr)
+  40     #       return curr+4
+  41     #     curr += row-size
+  42     #   abort
+  43     #
+  44     # . prolog
+  45     55/push-EBP
+  46     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  47     # . save registers
+  48     51/push-ECX
+  49     52/push-EDX
+  50     56/push-ESI
+  51     # ESI = table
+  52     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+  53     # curr/ECX = table->data
+  54     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
+  55     # max/EDX = table->data + table->write
+  56     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+  57     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
+  58 $get:search-loop:
+  59     # if (curr >= max) abort
+  60     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+  61     73/jump-if-greater-or-equal-unsigned  $get:abort/disp8
+  62     # if (string-equal?(key, *curr)) return curr+4
+  63     # . EAX = string-equal?(key, *curr)
+  64     # . . push args
+  65     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+  66     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+  67     # . . call
+  68     e8/call  string-equal?/disp32
+  69     # . . discard args
+  70     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  71     # . if (EAX != 0) return EAX = curr+4
+  72     3d/compare-EAX-and  0/imm32
+  73     74/jump-if-equal  $get:mismatch/disp8
+  74     8d/copy-address                 1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy ECX+4 to EAX
+  75     eb/jump  $get:end/disp8
+  76 $get:mismatch:
+  77     # curr += row-size
+  78     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x10/disp8      .                 # add *(EBP+16) to ECX
+  79     # loop
+  80     eb/jump  $get:search-loop/disp8
+  81 $get:end:
+  82     # . restore registers
+  83     5e/pop-to-ESI
+  84     5a/pop-to-EDX
+  85     59/pop-to-ECX
+  86     # . epilog
+  87     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+  88     5d/pop-to-EBP
+  89     c3/return
+  90 
+  91 $get:abort:
+  92     # . _write(2/stderr, error)
+  93     # . . push args
+  94     68/push  "get: key not found: "/imm32
+  95     68/push  2/imm32/stderr
+  96     # . . call
+  97     e8/call  _write/disp32
+  98     # . . discard args
+  99     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 100     # . _write(2/stderr, key)
+ 101     # . . push args
+ 102     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 103     68/push  2/imm32/stderr
+ 104     # . . call
+ 105     e8/call  _write/disp32
+ 106     # . . discard args
+ 107     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 108     # . syscall(exit, 1)
+ 109     bb/copy-to-EBX  1/imm32
+ 110     b8/copy-to-EAX  1/imm32/exit
+ 111     cd/syscall  0x80/imm8
+ 112     # never gets here
+ 113 
+ 114 test-get:
+ 115     # . prolog
+ 116     55/push-EBP
+ 117     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 118     # - setup: create a table with a couple of keys
+ 119     # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
+ 120     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
+ 121     68/push  0x10/imm32/length
+ 122     68/push  0/imm32/read
+ 123     68/push  0/imm32/write
+ 124     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 125     # insert(table, "code", 8 bytes per row)
+ 126     # . . push args
+ 127     68/push  8/imm32/row-size
+ 128     68/push  "code"/imm32
+ 129     51/push-ECX
+ 130     # . . call
+ 131     e8/call  get-or-insert/disp32
+ 132     # . . discard args
+ 133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 134     # insert(table, "data", 8 bytes per row)
+ 135     # . . push args
+ 136     68/push  8/imm32/row-size
+ 137     68/push  "data"/imm32
+ 138     51/push-ECX
+ 139     # . . call
+ 140     e8/call  get-or-insert/disp32
+ 141     # . . discard args
+ 142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 143 $test-get:check1:
+ 144     # EAX = get(table, "code", 8 bytes per row)
+ 145     # . . push args
+ 146     68/push  8/imm32/row-size
+ 147     68/push  "code"/imm32
+ 148     51/push-ECX
+ 149     # . . call
+ 150     e8/call  get/disp32
+ 151     # . . discard args
+ 152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 153     # check-ints-equal(EAX - table->data, 4, msg)
+ 154     # . check-ints-equal(EAX - table, 16, msg)
+ 155     # . . push args
+ 156     68/push  "F - test-get/0"/imm32
+ 157     68/push  0x10/imm32
+ 158     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 159     50/push-EAX
+ 160     # . . call
+ 161     e8/call  check-ints-equal/disp32
+ 162     # . . discard args
+ 163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 164 $test-get:check2:
+ 165     # EAX = get(table, "data", 8 bytes per row)
+ 166     # . . push args
+ 167     68/push  8/imm32/row-size
+ 168     68/push  "data"/imm32
+ 169     51/push-ECX
+ 170     # . . call
+ 171     e8/call  get/disp32
+ 172     # . . discard args
+ 173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 174     # check-ints-equal(EAX - table->data, 12, msg)
+ 175     # . check-ints-equal(EAX - table, 24, msg)
+ 176     # . . push args
+ 177     68/push  "F - test-get/1"/imm32
+ 178     68/push  0x18/imm32
+ 179     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 180     50/push-EAX
+ 181     # . . call
+ 182     e8/call  check-ints-equal/disp32
+ 183     # . . discard args
+ 184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 185 $test-get:end:
+ 186     # . epilog
+ 187     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 188     5d/pop-to-EBP
+ 189     c3/return
+ 190 
+ 191 # 'table' is a stream of (key, value) rows
+ 192 # keys are always strings (addresses; size 4 bytes)
+ 193 # values may be any type, but rows (key+value) always occupy 'row-size' bytes
+ 194 # scan 'table' for a row with a key 'key' and return the address of the corresponding value
+ 195 # if no row is found, abort
+ 196 get-slice:  # table : (address stream {string, _}), key : (address slice), row-size : int -> EAX : (address _)
+ 197     # pseudocode:
+ 198     #   curr = table->data
+ 199     #   max = &table->data[table->write]
+ 200     #   while curr < max
+ 201     #     if slice-equal?(key, *curr)
+ 202     #       return curr+4
+ 203     #     curr += row-size
+ 204     #   abort
+ 205     #
+ 206     # . prolog
+ 207     55/push-EBP
+ 208     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 209     # . save registers
+ 210     51/push-ECX
+ 211     52/push-EDX
+ 212     56/push-ESI
+ 213     # ESI = table
+ 214     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 215     # curr/ECX = table->data
+ 216     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
+ 217     # max/EDX = table->data + table->write
+ 218     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 219     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
+ 220 $get-slice:search-loop:
+ 221     # if (curr >= max) abort
+ 222     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 223     73/jump-if-greater-or-equal-unsigned  $get-slice:abort/disp8
+ 224     # if (slice-equal?(key, *curr)) return curr+4
+ 225     # . EAX = slice-equal?(key, *curr)
+ 226     # . . push args
+ 227     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 228     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 229     # . . call
+ 230     e8/call  slice-equal?/disp32
+ 231     # . . discard args
+ 232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 233     # . if (EAX != 0) return EAX = curr+4
+ 234     3d/compare-EAX-and  0/imm32
+ 235     74/jump-if-equal  $get-slice:mismatch/disp8
+ 236     8d/copy-address                 1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy ECX+4 to EAX
+ 237     eb/jump  $get-slice:end/disp8
+ 238 $get-slice:mismatch:
+ 239     # curr += row-size
+ 240     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x10/disp8      .                 # add *(EBP+16) to ECX
+ 241     # loop
+ 242     eb/jump  $get-slice:search-loop/disp8
+ 243 $get-slice:end:
+ 244     # . restore registers
+ 245     5e/pop-to-ESI
+ 246     5a/pop-to-EDX
+ 247     59/pop-to-ECX
+ 248     # . epilog
+ 249     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 250     5d/pop-to-EBP
+ 251     c3/return
+ 252 
+ 253 $get-slice:abort:
+ 254     # . _write(2/stderr, error)
+ 255     # . . push args
+ 256     68/push  "get-slice: key not found: "/imm32
+ 257     68/push  2/imm32/stderr
+ 258     # . . call
+ 259     e8/call  _write/disp32
+ 260     # . . discard args
+ 261     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 262     # . write-slice-buffered(Stderr, key)
+ 263     # . . push args
+ 264     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 265     68/push  Stderr/imm32
+ 266     # . . call
+ 267     e8/call  write-slice-buffered/disp32
+ 268     # . . discard args
+ 269     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 270     # . flush(Stderr)
+ 271     # . . push args
+ 272     68/push  Stderr/imm32
+ 273     # . . call
+ 274     e8/call  flush/disp32
+ 275     # . . discard args
+ 276     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 277     # . syscall(exit, 1)
+ 278     bb/copy-to-EBX  1/imm32
+ 279     b8/copy-to-EAX  1/imm32/exit
+ 280     cd/syscall  0x80/imm8
+ 281     # never gets here
+ 282 
+ 283 test-get-slice:
+ 284     # . prolog
+ 285     55/push-EBP
+ 286     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 287     # - setup: create a table with a couple of keys
+ 288     # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
+ 289     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
+ 290     68/push  0x10/imm32/length
+ 291     68/push  0/imm32/read
+ 292     68/push  0/imm32/write
+ 293     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 294     # insert(table, "code", 8 bytes per row)
+ 295     # . . push args
+ 296     68/push  8/imm32/row-size
+ 297     68/push  "code"/imm32
+ 298     51/push-ECX
+ 299     # . . call
+ 300     e8/call  get-or-insert/disp32
+ 301     # . . discard args
+ 302     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 303     # insert(table, "data", 8 bytes per row)
+ 304     # . . push args
+ 305     68/push  8/imm32/row-size
+ 306     68/push  "data"/imm32
+ 307     51/push-ECX
+ 308     # . . call
+ 309     e8/call  get-or-insert/disp32
+ 310     # . . discard args
+ 311     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 312 $test-get-slice:check1:
+ 313     # (EAX..EDX) = "code"
+ 314     b8/copy-to-EAX  "code"/imm32
+ 315     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy *EAX to EDX
+ 316     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  2/index/EDX   .           2/r32/EDX   4/disp8         .                 # copy EAX+EDX+4 to EDX
+ 317     05/add-to-EAX  4/imm32
+ 318     # var slice/EDX = {EAX, EDX}
+ 319     52/push-EDX
+ 320     50/push-EAX
+ 321     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 322     # EAX = get-slice(table, "code", 8 bytes per row)
+ 323     # . . push args
+ 324     68/push  8/imm32/row-size
+ 325     52/push-EDX
+ 326     51/push-ECX
+ 327     # . . call
+ 328     e8/call  get-slice/disp32
+ 329     # . . discard args
+ 330     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 331     # check-ints-equal(EAX - table->data, 4, msg)  # first row's value slot returned
+ 332     # . check-ints-equal(EAX - table, 16, msg)
+ 333     # . . push args
+ 334     68/push  "F - test-get-slice/0"/imm32
+ 335     68/push  0x10/imm32
+ 336     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 337     50/push-EAX
+ 338     # . . call
+ 339     e8/call  check-ints-equal/disp32
+ 340     # . . discard args
+ 341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 342 $test-get-slice:check2:
+ 343     # (EAX..EDX) = "data"
+ 344     b8/copy-to-EAX  "data"/imm32
+ 345     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy *EAX to EDX
+ 346     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  2/index/EDX   .           2/r32/EDX   4/disp8         .                 # copy EAX+EDX+4 to EDX
+ 347     05/add-to-EAX  4/imm32
+ 348     # var slice/EDX = {EAX, EDX}
+ 349     52/push-EDX
+ 350     50/push-EAX
+ 351     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 352     # EAX = get-slice(table, "data" slice, 8 bytes per row)
+ 353     # . . push args
+ 354     68/push  8/imm32/row-size
+ 355     52/push-EDX
+ 356     51/push-ECX
+ 357     # . . call
+ 358     e8/call  get-slice/disp32
+ 359     # . . discard args
+ 360     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 361     # check-ints-equal(EAX - table->data, 12, msg)
+ 362     # . check-ints-equal(EAX - table, 24, msg)
+ 363     # . . push args
+ 364     68/push  "F - test-get-slice/1"/imm32
+ 365     68/push  0x18/imm32
+ 366     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 367     50/push-EAX
+ 368     # . . call
+ 369     e8/call  check-ints-equal/disp32
+ 370     # . . discard args
+ 371     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 372 $test-get-slice:end:
+ 373     # . epilog
+ 374     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 375     5d/pop-to-EBP
+ 376     c3/return
+ 377 
+ 378 # 'table' is a stream of (key, value) rows
+ 379 # keys are always strings (addresses; size 4 bytes)
+ 380 # values may be any type, but rows (key+value) always occupy 'row-size' bytes
+ 381 # scan 'table' for a row with a key 'key' and return the address of the corresponding value
+ 382 # if no row is found, save 'key' to the next available row
+ 383 # if there are no rows free, abort
+ 384 # return the address of the value
+ 385 # Beware: assume keys are immutable; they're inserted by reference
+ 386 # TODO: pass in an allocation descriptor
+ 387 get-or-insert:  # table : (address stream {string, _}), key : (address string), row-size : int -> EAX : (address _)
+ 388     # pseudocode:
+ 389     #   curr = table->data
+ 390     #   max = &table->data[table->write]
+ 391     #   while curr < max
+ 392     #     if string-equal?(key, *curr)
+ 393     #       return curr+4
+ 394     #     curr += row-size
+ 395     #   if table->write >= table->length
+ 396     #     abort
+ 397     #   zero-out(max, row-size)
+ 398     #   *max = key
+ 399     #   table->write += row-size
+ 400     #   return max+4
+ 401     #
+ 402     # . prolog
+ 403     55/push-EBP
+ 404     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 405     # . save registers
+ 406     51/push-ECX
+ 407     52/push-EDX
+ 408     56/push-ESI
+ 409     # ESI = table
+ 410     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 411     # curr/ECX = table->data
+ 412     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
+ 413     # max/EDX = table->data + table->write
+ 414     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 415     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
+ 416 $get-or-insert:search-loop:
+ 417     # if (curr >= max) break
+ 418     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 419     73/jump-if-greater-or-equal-unsigned  $get-or-insert:not-found/disp8
+ 420     # if (string-equal?(key, *curr)) return curr+4
+ 421     # . EAX = string-equal?(key, *curr)
+ 422     # . . push args
+ 423     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 424     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 425     # . . call
+ 426     e8/call  string-equal?/disp32
+ 427     # . . discard args
+ 428     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 429     # . if (EAX != 0) return EAX = curr+4
+ 430     3d/compare-EAX-and  0/imm32
+ 431     74/jump-if-equal  $get-or-insert:mismatch/disp8
+ 432     8d/copy-address                 1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy ECX+4 to EAX
+ 433     eb/jump  $get-or-insert:end/disp8
+ 434 $get-or-insert:mismatch:
+ 435     # curr += row-size
+ 436     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x10/disp8      .                 # add *(EBP+16) to ECX
+ 437     # loop
+ 438     eb/jump  $get-or-insert:search-loop/disp8
+ 439 $get-or-insert:not-found:
+ 440     # result/EAX = 0
+ 441     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 442     # if (table->write >= table->length) abort
+ 443     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+ 444     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
+ 445     73/jump-if-greater-or-equal-unsigned  $get-or-insert:abort/disp8
+ 446     # zero-out(max, row-size)
+ 447     # . . push args
+ 448     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+ 449     52/push-EDX
+ 450     # . . call
+ 451     e8/call  zero-out/disp32
+ 452     # . . discard args
+ 453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 454     # *max = key
+ 455     # . EAX = key
+ 456     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
+ 457     # . *max = EAX
+ 458     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
+ 459     # table->write += row-size
+ 460     # . EAX = row-size
+ 461     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+ 462     # . table->write += EAX
+ 463     01/add                          0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # add EAX to *ESI
+ 464     # return max+4
+ 465     # . EAX = max
+ 466     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy EDX to EAX
+ 467     # . EAX += 4
+ 468     05/add-to-EAX  4/imm32
+ 469 $get-or-insert:end:
+ 470     # . restore registers
+ 471     5e/pop-to-ESI
+ 472     5a/pop-to-EDX
+ 473     59/pop-to-ECX
+ 474     # . epilog
+ 475     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 476     5d/pop-to-EBP
+ 477     c3/return
+ 478 
+ 479 $get-or-insert:abort:
+ 480     # . _write(2/stderr, error)
+ 481     # . . push args
+ 482     68/push  "get-or-insert: too many segments"/imm32
+ 483     68/push  2/imm32/stderr
+ 484     # . . call
+ 485     e8/call  _write/disp32
+ 486     # . . discard args
+ 487     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 488     # . syscall(exit, 1)
+ 489     bb/copy-to-EBX  1/imm32
+ 490     b8/copy-to-EAX  1/imm32/exit
+ 491     cd/syscall  0x80/imm8
+ 492     # never gets here
+ 493 
+ 494 test-get-or-insert:
+ 495     # . prolog
+ 496     55/push-EBP
+ 497     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 498     # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
+ 499     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
+ 500     68/push  0x10/imm32/length
+ 501     68/push  0/imm32/read
+ 502     68/push  0/imm32/write
+ 503     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 504 $test-get-or-insert:first-call:
+ 505     # - start with an empty table, insert one key, verify that it was inserted
+ 506     # EAX = get-or-insert(table, "code", 8 bytes per row)
+ 507     # . . push args
+ 508     68/push  8/imm32/row-size
+ 509     68/push  "code"/imm32
+ 510     51/push-ECX
+ 511     # . . call
+ 512     e8/call  get-or-insert/disp32
+ 513     # . . discard args
+ 514     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 515     # check-ints-equal(EAX - table->data, 4, msg)  # first row's value slot returned
+ 516     # . check-ints-equal(EAX - table, 16, msg)
+ 517     # . . push args
+ 518     68/push  "F - test-get-or-insert/0"/imm32
+ 519     68/push  0x10/imm32
+ 520     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 521     50/push-EAX
+ 522     # . . call
+ 523     e8/call  check-ints-equal/disp32
+ 524     # . . discard args
+ 525     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 526 $test-get-or-insert:check2:
+ 527     # check-ints-equal(table->write, row-size = 8, msg)
+ 528     # . . push args
+ 529     68/push  "F - test-get-or-insert/1"/imm32
+ 530     68/push  8/imm32/row-size
+ 531     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 532     # . . call
+ 533     e8/call  check-ints-equal/disp32
+ 534     # . . discard args
+ 535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 536     # check-string-equal(*table->data, "code", msg)
+ 537     # . . push args
+ 538     68/push  "F - test-get-or-insert/2"/imm32
+ 539     68/push  "code"/imm32
+ 540     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
+ 541     # . . call
+ 542     e8/call  check-string-equal/disp32
+ 543     # . . discard args
+ 544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 545 $test-get-or-insert:second-call:
+ 546     # - insert the same key again, verify that it was reused
+ 547     # EAX = get-or-insert(table, "code", 8 bytes per row)
+ 548     # . . push args
+ 549     68/push  8/imm32/row-size
+ 550     68/push  "code"/imm32
+ 551     51/push-ECX
+ 552     # . . call
+ 553     e8/call  get-or-insert/disp32
+ 554     # . . discard args
+ 555     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 556     # check-ints-equal(EAX - table->data, 4, msg)
+ 557     # . check-ints-equal(EAX - table, 16, msg)
+ 558     # . . push args
+ 559     68/push  "F - test-get-or-insert/3"/imm32
+ 560     68/push  0x10/imm32
+ 561     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 562     50/push-EAX
+ 563     # . . call
+ 564     e8/call  check-ints-equal/disp32
+ 565     # . . discard args
+ 566     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 567     # no new row inserted
+ 568     # . check-ints-equal(table->write, row-size = 8, msg)
+ 569     # . . push args
+ 570     68/push  "F - test-get-or-insert/4"/imm32
+ 571     68/push  8/imm32/row-size
+ 572     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 573     # . . call
+ 574     e8/call  check-ints-equal/disp32
+ 575     # . . discard args
+ 576     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 577     # check-string-equal(*table->data, "code", msg)
+ 578     # . . push args
+ 579     68/push  "F - test-get-or-insert/5"/imm32
+ 580     68/push  "code"/imm32
+ 581     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
+ 582     # . . call
+ 583     e8/call  check-string-equal/disp32
+ 584     # . . discard args
+ 585     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 586 $test-get-or-insert:third-call:
+ 587     # - insert a new key, verify that it was inserted
+ 588     # EAX = get-or-insert(table, "data", 8 bytes per row)
+ 589     # . . push args
+ 590     68/push  8/imm32/row-size
+ 591     68/push  "data"/imm32
+ 592     51/push-ECX
+ 593     # . . call
+ 594     e8/call  get-or-insert/disp32
+ 595     # . . discard args
+ 596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 597     # table gets a new row
+ 598     # check-ints-equal(EAX - table->data, 12, msg)  # second row's value slot returned
+ 599     # . check-ints-equal(EAX - table, 24, msg)
+ 600     # . . push args
+ 601     68/push  "F - test-get-or-insert/6"/imm32
+ 602     68/push  0x18/imm32
+ 603     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 604     50/push-EAX
+ 605     # . . call
+ 606     e8/call  check-ints-equal/disp32
+ 607     # . . discard args
+ 608     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 609     # check-ints-equal(table->write, 2 rows = 16, msg)
+ 610     # . . push args
+ 611     68/push  "F - test-get-or-insert/7"/imm32
+ 612     68/push  0x10/imm32/two-rows
+ 613     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 614     # . . call
+ 615     e8/call  check-ints-equal/disp32
+ 616     # . . discard args
+ 617     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 618     # check-string-equal(*table->data+8, "data", msg)
+ 619     # check-string-equal(*(table+20), "data", msg)
+ 620     # . . push args
+ 621     68/push  "F - test-get-or-insert/8"/imm32
+ 622     68/push  "data"/imm32
+ 623     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0x14/disp8      .                 # push *(ECX+20)
+ 624     # . . call
+ 625     e8/call  check-string-equal/disp32
+ 626     # . . discard args
+ 627     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 628 $test-get-or-insert:end:
+ 629     # . epilog
+ 630     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 631     5d/pop-to-EBP
+ 632     c3/return
+ 633 
+ 634 # 'table' is a stream of (key, value) rows
+ 635 # keys are always strings (addresses; size 4 bytes)
+ 636 # values may be any type, but rows (key+value) always occupy 'row-size' bytes
+ 637 # scan 'table' for a row with a key 'key' and return the address of the corresponding value
+ 638 # if no row is found, save 'key' in the next available row
+ 639 # if there are no rows free, abort
+ 640 # WARNING: leaks memory
+ 641 # TODO: pass in an allocation descriptor
+ 642 leaky-get-or-insert-slice:  # table : (address stream {string, _}), key : (address slice), row-size : int -> EAX : (address _)
+ 643     # pseudocode:
+ 644     #   curr = table->data
+ 645     #   max = &table->data[table->write]
+ 646     #   while curr < max
+ 647     #     if slice-equal?(key, *curr)
+ 648     #       return curr+4
+ 649     #     curr += row-size
+ 650     #   if table->write >= table->length
+ 651     #     abort
+ 652     #   zero-out(max, row-size)
+ 653     #   *max = slice-to-string(Heap, key)
+ 654     #   table->write += row-size
+ 655     #   return max+4
+ 656     #
+ 657     # . prolog
+ 658     55/push-EBP
+ 659     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 660     # . save registers
+ 661     51/push-ECX
+ 662     52/push-EDX
+ 663     56/push-ESI
+ 664     # ESI = table
+ 665     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 666     # curr/ECX = table->data
+ 667     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
+ 668     # max/EDX = table->data + table->write
+ 669     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 670     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
+ 671 $leaky-get-or-insert-slice:search-loop:
+ 672     # if (curr >= max) break
+ 673     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 674     73/jump-if-greater-or-equal-unsigned  $leaky-get-or-insert-slice:not-found/disp8
+ 675     # if (slice-equal?(key, *curr)) return curr+4
+ 676     # . EAX = slice-equal?(key, *curr)
+ 677     # . . push args
+ 678     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 679     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 680     # . . call
+ 681     e8/call  slice-equal?/disp32
+ 682     # . . discard args
+ 683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 684     # . if (EAX != 0) return EAX = curr+4
+ 685     3d/compare-EAX-and  0/imm32
+ 686     74/jump-if-equal  $leaky-get-or-insert-slice:mismatch/disp8
+ 687     8d/copy-address                 1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy ECX+4 to EAX
+ 688     eb/jump  $leaky-get-or-insert-slice:end/disp8
+ 689 $leaky-get-or-insert-slice:mismatch:
+ 690     # curr += row-size
+ 691     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x10/disp8      .                 # add *(EBP+16) to ECX
+ 692     # loop
+ 693     eb/jump  $leaky-get-or-insert-slice:search-loop/disp8
+ 694 $leaky-get-or-insert-slice:not-found:
+ 695     # result/EAX = 0
+ 696     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 697     # if (table->write >= table->length) abort
+ 698     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+ 699     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
+ 700     7d/jump-if-greater-or-equal  $leaky-get-or-insert-slice:abort/disp8
+ 701     # zero-out(max, row-size)
+ 702     # . . push args
+ 703     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+ 704     52/push-EDX
+ 705     # . . call
+ 706     e8/call  zero-out/disp32
+ 707     # . . discard args
+ 708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 709     # *max = slice-to-string(Heap, key)
+ 710     # . EAX = slice-to-string(Heap, key)
+ 711     # . . push args
+ 712     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 713     68/push  Heap/imm32
+ 714     # . . call
+ 715     e8/call  slice-to-string/disp32
+ 716     # . . discard args
+ 717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 718     # . *max = EAX
+ 719     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
+ 720     # table->write += row-size
+ 721     # . EAX = row-size
+ 722     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+ 723     # . table->write += EAX
+ 724     01/add                          0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # add EAX to *ESI
+ 725     # return max+4
+ 726     # . EAX = max
+ 727     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy EDX to EAX
+ 728     # . EAX += 4
+ 729     05/add-to-EAX  4/imm32
+ 730 $leaky-get-or-insert-slice:end:
+ 731     # . restore registers
+ 732     5e/pop-to-ESI
+ 733     5a/pop-to-EDX
+ 734     59/pop-to-ECX
+ 735     # . epilog
+ 736     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 737     5d/pop-to-EBP
+ 738     c3/return
+ 739 
+ 740 $leaky-get-or-insert-slice:abort:
+ 741     # . _write(2/stderr, error)
+ 742     # . . push args
+ 743     68/push  "leaky-get-or-insert-slice: too many segments"/imm32
+ 744     68/push  2/imm32/stderr
+ 745     # . . call
+ 746     e8/call  _write/disp32
+ 747     # . . discard args
+ 748     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 749     # . syscall(exit, 1)
+ 750     bb/copy-to-EBX  1/imm32
+ 751     b8/copy-to-EAX  1/imm32/exit
+ 752     cd/syscall  0x80/imm8
+ 753     # never gets here
+ 754 
+ 755 test-leaky-get-or-insert-slice:
+ 756     # . prolog
+ 757     55/push-EBP
+ 758     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 759     # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
+ 760     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
+ 761     68/push  0x10/imm32/length
+ 762     68/push  0/imm32/read
+ 763     68/push  0/imm32/write
+ 764     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 765     # (EAX..EDX) = "code"
+ 766     b8/copy-to-EAX  "code"/imm32
+ 767     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy *EAX to EDX
+ 768     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  2/index/EDX   .           2/r32/EDX   4/disp8         .                 # copy EAX+EDX+4 to EDX
+ 769     05/add-to-EAX  4/imm32
+ 770     # var slice/EDX = {EAX, EDX}
+ 771     52/push-EDX
+ 772     50/push-EAX
+ 773     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 774 $test-leaky-get-or-insert-slice:first-call:
+ 775     # - start with an empty table, insert one key, verify that it was inserted
+ 776     # EAX = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row)
+ 777     # . . push args
+ 778     68/push  8/imm32/row-size
+ 779     52/push-EDX
+ 780     51/push-ECX
+ 781     # . . call
+ 782     e8/call  leaky-get-or-insert-slice/disp32
+ 783     # . . discard args
+ 784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 785     # check-ints-equal(EAX - table->data, 4, msg)  # first row's value slot returned
+ 786     # . check-ints-equal(EAX - table, 16, msg)
+ 787     # . . push args
+ 788     68/push  "F - test-leaky-get-or-insert-slice/0"/imm32
+ 789     68/push  0x10/imm32
+ 790     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 791     50/push-EAX
+ 792     # . . call
+ 793     e8/call  check-ints-equal/disp32
+ 794     # . . discard args
+ 795     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 796 $test-leaky-get-or-insert-slice:check2:
+ 797     # check-ints-equal(table->write, row-size = 8, msg)
+ 798     # . . push args
+ 799     68/push  "F - test-leaky-get-or-insert-slice/1"/imm32
+ 800     68/push  8/imm32/row-size
+ 801     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 802     # . . call
+ 803     e8/call  check-ints-equal/disp32
+ 804     # . . discard args
+ 805     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 806     # check-string-equal(*table->data, "code", msg)
+ 807     # . . push args
+ 808     68/push  "F - test-leaky-get-or-insert-slice/2"/imm32
+ 809     68/push  "code"/imm32
+ 810     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
+ 811     # . . call
+ 812     e8/call  check-string-equal/disp32
+ 813     # . . discard args
+ 814     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 815 $test-leaky-get-or-insert-slice:second-call:
+ 816     # - insert the same key again, verify that it was reused
+ 817     # EAX = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row)
+ 818     # . . push args
+ 819     68/push  8/imm32/row-size
+ 820     52/push-EDX
+ 821     51/push-ECX
+ 822     # . . call
+ 823     e8/call  leaky-get-or-insert-slice/disp32
+ 824     # . . discard args
+ 825     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 826     # check-ints-equal(EAX - table->data, 4, msg)
+ 827     # . check-ints-equal(EAX - table, 16, msg)
+ 828     # . . push args
+ 829     68/push  "F - test-leaky-get-or-insert-slice/3"/imm32
+ 830     68/push  0x10/imm32
+ 831     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 832     50/push-EAX
+ 833     # . . call
+ 834     e8/call  check-ints-equal/disp32
+ 835     # . . discard args
+ 836     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 837     # no new row inserted
+ 838     # . check-ints-equal(table->write, row-size = 8, msg)
+ 839     # . . push args
+ 840     68/push  "F - test-leaky-get-or-insert-slice/4"/imm32
+ 841     68/push  8/imm32/row-size
+ 842     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 843     # . . call
+ 844     e8/call  check-ints-equal/disp32
+ 845     # . . discard args
+ 846     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 847     # check-string-equal(*table->data, "code", msg)
+ 848     # . . push args
+ 849     68/push  "F - test-leaky-get-or-insert-slice/5"/imm32
+ 850     68/push  "code"/imm32
+ 851     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
+ 852     # . . call
+ 853     e8/call  check-string-equal/disp32
+ 854     # . . discard args
+ 855     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 856 $test-leaky-get-or-insert-slice:third-call:
+ 857     # - insert a new key, verify that it was inserted
+ 858     # (EAX..EDX) = "data"
+ 859     b8/copy-to-EAX  "data"/imm32
+ 860     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy *EAX to EDX
+ 861     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  2/index/EDX   .           2/r32/EDX   4/disp8         .                 # copy EAX+EDX+4 to EDX
+ 862     05/add-to-EAX  4/imm32
+ 863     # var slice/EDX = {EAX, EDX}
+ 864     52/push-EDX
+ 865     50/push-EAX
+ 866     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 867     # EAX = leaky-get-or-insert-slice(table, "data" slice, 8 bytes per row)
+ 868     # . . push args
+ 869     68/push  8/imm32/row-size
+ 870     52/push-EDX
+ 871     51/push-ECX
+ 872     # . . call
+ 873     e8/call  leaky-get-or-insert-slice/disp32
+ 874     # . . discard args
+ 875     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 876     # table gets a new row
+ 877     # check-ints-equal(EAX - table->data, 12, msg)  # second row's value slot returned
+ 878     # . check-ints-equal(EAX - table, 24, msg)
+ 879     # . . push args
+ 880     68/push  "F - test-leaky-get-or-insert-slice/6"/imm32
+ 881     68/push  0x18/imm32
+ 882     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+ 883     50/push-EAX
+ 884     # . . call
+ 885     e8/call  check-ints-equal/disp32
+ 886     # . . discard args
+ 887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 888     # check-ints-equal(table->write, 2 rows = 16, msg)
+ 889     # . . push args
+ 890     68/push  "F - test-leaky-get-or-insert-slice/7"/imm32
+ 891     68/push  0x10/imm32/two-rows
+ 892     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 893     # . . call
+ 894     e8/call  check-ints-equal/disp32
+ 895     # . . discard args
+ 896     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 897     # check-string-equal(*table->data+8, "data", msg)
+ 898     # check-string-equal(*(table+20), "data", msg)
+ 899     # . . push args
+ 900     68/push  "F - test-leaky-get-or-insert-slice/8"/imm32
+ 901     68/push  "data"/imm32
+ 902     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0x14/disp8      .                 # push *(ECX+20)
+ 903     # . . call
+ 904     e8/call  check-string-equal/disp32
+ 905     # . . discard args
+ 906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 907 $test-leaky-get-or-insert-slice:end:
+ 908     # . epilog
+ 909     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 910     5d/pop-to-EBP
+ 911     c3/return
+ 912 
+ 913 # (re)compute the bounds of the next word in the line
+ 914 # return empty string on reaching end of file
+ 915 next-word:  # line : (address stream byte), out : (address slice)
+ 916     # . prolog
+ 917     55/push-EBP
+ 918     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 919     # . save registers
+ 920     50/push-EAX
+ 921     51/push-ECX
+ 922     56/push-ESI
+ 923     57/push-EDI
+ 924     # ESI = line
+ 925     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 926     # EDI = out
+ 927     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+ 928     # skip-chars-matching(line, ' ')
+ 929     # . . push args
+ 930     68/push  0x20/imm32/space
+ 931     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 932     # . . call
+ 933     e8/call  skip-chars-matching/disp32
+ 934     # . . discard args
+ 935     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 936 $next-word:check0:
+ 937     # if (line->read >= line->write) clear out and return
+ 938     # . EAX = line->read
+ 939     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+ 940     # . if (EAX < line->write) goto next check
+ 941     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
+ 942     7c/jump-if-lesser  $next-word:check-for-comment/disp8
+ 943     # . return out = {0, 0}
+ 944     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
+ 945     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
+ 946     eb/jump  $next-word:end/disp8
+ 947 $next-word:check-for-comment:
+ 948     # out->start = &line->data[line->read]
+ 949     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 950     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+ 951     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+ 952     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
+ 953     # . EAX = line->data[line->read]
+ 954     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 955     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
+ 956     # . compare
+ 957     3d/compare-EAX-and  0x23/imm32/pound
+ 958     75/jump-if-not-equal  $next-word:regular-word/disp8
+ 959 $next-word:comment:
+ 960     # . out->end = &line->data[line->write]
+ 961     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+ 962     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
+ 963     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+ 964     # . line->read = line->write
+ 965     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
+ 966     # . return
+ 967     eb/jump  $next-word:end/disp8
+ 968 $next-word:regular-word:
+ 969     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
+ 970     # . . push args
+ 971     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 972     # . . call
+ 973     e8/call  skip-chars-not-matching-whitespace/disp32
+ 974     # . . discard args
+ 975     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 976     # out->end = &line->data[line->read]
+ 977     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 978     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+ 979     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+ 980 $next-word:end:
+ 981     # . restore registers
+ 982     5f/pop-to-EDI
+ 983     5e/pop-to-ESI
+ 984     59/pop-to-ECX
+ 985     58/pop-to-EAX
+ 986     # . epilog
+ 987     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 988     5d/pop-to-EBP
+ 989     c3/return
+ 990 
+ 991 test-next-word:
+ 992     # . prolog
+ 993     55/push-EBP
+ 994     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 995     # setup
+ 996     # . clear-stream(_test-stream)
+ 997     # . . push args
+ 998     68/push  _test-stream/imm32
+ 999     # . . call
+1000     e8/call  clear-stream/disp32
+1001     # . . discard args
+1002     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1003     # var slice/ECX = {0, 0}
+1004     68/push  0/imm32/end
+1005     68/push  0/imm32/start
+1006     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1007     # write(_test-stream, "  ab")
+1008     # . . push args
+1009     68/push  "  ab"/imm32
+1010     68/push  _test-stream/imm32
+1011     # . . call
+1012     e8/call  write/disp32
+1013     # . . discard args
+1014     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1015     # next-word(_test-stream, slice)
+1016     # . . push args
+1017     51/push-ECX
+1018     68/push  _test-stream/imm32
+1019     # . . call
+1020     e8/call  next-word/disp32
+1021     # . . discard args
+1022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1023     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+1024     # . check-ints-equal(slice->start - _test-stream, 14, msg)
+1025     # . . push args
+1026     68/push  "F - test-next-word: start"/imm32
+1027     68/push  0xe/imm32
+1028     # . . push slice->start - _test-stream
+1029     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+1030     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+1031     50/push-EAX
+1032     # . . call
+1033     e8/call  check-ints-equal/disp32
+1034     # . . discard args
+1035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1036     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
+1037     # . check-ints-equal(slice->end - _test-stream, 16, msg)
+1038     # . . push args
+1039     68/push  "F - test-next-word: end"/imm32
+1040     68/push  0x10/imm32
+1041     # . . push slice->end - _test-stream
+1042     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1043     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+1044     50/push-EAX
+1045     # . . call
+1046     e8/call  check-ints-equal/disp32
+1047     # . . discard args
+1048     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1049     # . epilog
+1050     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1051     5d/pop-to-EBP
+1052     c3/return
+1053 
+1054 test-next-word-returns-whole-comment:
+1055     # . prolog
+1056     55/push-EBP
+1057     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1058     # setup
+1059     # . clear-stream(_test-stream)
+1060     # . . push args
+1061     68/push  _test-stream/imm32
+1062     # . . call
+1063     e8/call  clear-stream/disp32
+1064     # . . discard args
+1065     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1066     # var slice/ECX = {0, 0}
+1067     68/push  0/imm32/end
+1068     68/push  0/imm32/start
+1069     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1070     # write(_test-stream, "  # a")
+1071     # . . push args
+1072     68/push  "  # a"/imm32
+1073     68/push  _test-stream/imm32
+1074     # . . call
+1075     e8/call  write/disp32
+1076     # . . discard args
+1077     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1078     # next-word(_test-stream, slice)
+1079     # . . push args
+1080     51/push-ECX
+1081     68/push  _test-stream/imm32
+1082     # . . call
+1083     e8/call  next-word/disp32
+1084     # . . discard args
+1085     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1086     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+1087     # . check-ints-equal(slice->start - _test-stream, 14, msg)
+1088     # . . push args
+1089     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
+1090     68/push  0xe/imm32
+1091     # . . push slice->start - _test-stream
+1092     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+1093     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+1094     50/push-EAX
+1095     # . . call
+1096     e8/call  check-ints-equal/disp32
+1097     # . . discard args
+1098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1099     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
+1100     # . check-ints-equal(slice->end - _test-stream, 17, msg)
+1101     # . . push args
+1102     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
+1103     68/push  0x11/imm32
+1104     # . . push slice->end - _test-stream
+1105     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1106     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+1107     50/push-EAX
+1108     # . . call
+1109     e8/call  check-ints-equal/disp32
+1110     # . . discard args
+1111     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1112     # . epilog
+1113     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1114     5d/pop-to-EBP
+1115     c3/return
+1116 
+1117 test-next-word-returns-empty-string-on-eof:
+1118     # . prolog
+1119     55/push-EBP
+1120     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1121     # setup
+1122     # . clear-stream(_test-stream)
+1123     # . . push args
+1124     68/push  _test-stream/imm32
+1125     # . . call
+1126     e8/call  clear-stream/disp32
+1127     # . . discard args
+1128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1129     # var slice/ECX = {0, 0}
+1130     68/push  0/imm32/end
+1131     68/push  0/imm32/start
+1132     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1133     # write nothing to _test-stream
+1134     # next-word(_test-stream, slice)
+1135     # . . push args
+1136     51/push-ECX
+1137     68/push  _test-stream/imm32
+1138     # . . call
+1139     e8/call  next-word/disp32
+1140     # . . discard args
+1141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1142     # check-ints-equal(slice->end - slice->start, 0, msg)
+1143     # . . push args
+1144     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
+1145     68/push  0/imm32
+1146     # . . push slice->end - slice->start
+1147     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1148     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
+1149     50/push-EAX
+1150     # . . call
+1151     e8/call  check-ints-equal/disp32
+1152     # . . discard args
+1153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1154     # . epilog
+1155     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1156     5d/pop-to-EBP
+1157     c3/return
+1158 
+1159 # write an entire stream's contents to a buffered-file
+1160 # ways to do this:
+1161 #   - construct a 'maximal slice' and pass it to write-slice-buffered
+1162 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
+1163 # we'll go with the first way for now
+1164 write-stream-data:  # f : (address buffered-file), s : (address stream) -> <void>
+1165     # . prolog
+1166     55/push-EBP
+1167     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1168     # . save registers
+1169     50/push-EAX
+1170     51/push-ECX
+1171     56/push-ESI
+1172     # ESI = s
+1173     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+1174     # var slice/ECX = {s->data, s->data + s->write}
+1175     # . push s->data + s->write
+1176     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+1177     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
+1178     50/push-EAX
+1179     # . push s->data
+1180     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
+1181     50/push-EAX
+1182     # . ECX = ESP
+1183     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1184     # write-slice-buffered(f, slice)
+1185     # . . push args
+1186     51/push-ECX
+1187     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1188     # . . call
+1189     e8/call  write-slice-buffered/disp32
+1190     # . . discard args
+1191     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1192 $write-stream-data:end:
+1193     # . restore locals
+1194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1195     # . restore registers
+1196     5e/pop-to-ESI
+1197     59/pop-to-ECX
+1198     58/pop-to-EAX
+1199     # . epilog
+1200     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1201     5d/pop-to-EBP
+1202     c3/return
+1203 
+1204 test-write-stream-data:
+1205     # . prolog
+1206     55/push-EBP
+1207     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1208     # setup
+1209     # . clear-stream(_test-output-stream)
+1210     # . . push args
+1211     68/push  _test-output-stream/imm32
+1212     # . . call
+1213     e8/call  clear-stream/disp32
+1214     # . . discard args
+1215     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1216     # . clear-stream(_test-output-buffered-file+4)
+1217     # . . push args
+1218     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1219     05/add-to-EAX  4/imm32
+1220     50/push-EAX
+1221     # . . call
+1222     e8/call  clear-stream/disp32
+1223     # . . discard args
+1224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1225     # . clear-stream(_test-input-stream)
+1226     # . . push args
+1227     68/push  _test-input-stream/imm32
+1228     # . . call
+1229     e8/call  clear-stream/disp32
+1230     # . . discard args
+1231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1232     # initialize input
+1233     # . write(_test-input-stream, "abcd")
+1234     # . . push args
+1235     68/push  "abcd"/imm32
+1236     68/push  _test-input-stream/imm32
+1237     # . . call
+1238     e8/call  write/disp32
+1239     # . . discard args
+1240     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1241     # write-stream-data(_test-output-buffered-file, _test-input-stream)
+1242     # . . push args
+1243     68/push  _test-input-stream/imm32
+1244     68/push  _test-output-buffered-file/imm32
+1245     # . . call
+1246     e8/call  write-stream-data/disp32
+1247     # . . discard args
+1248     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1249     # check that the write happened as expected
+1250     # . flush(_test-output-buffered-file)
+1251     # . . push args
+1252     68/push  _test-output-buffered-file/imm32
+1253     # . . call
+1254     e8/call  flush/disp32
+1255     # . . discard args
+1256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1257     # . check-stream-equal(_test-output-stream, "abcd", msg)
+1258     # . . push args
+1259     68/push  "F - test-write-stream-data"/imm32
+1260     68/push  "abcd"/imm32
+1261     68/push  _test-output-stream/imm32
+1262     # . . call
+1263     e8/call  check-stream-equal/disp32
+1264     # . . discard args
+1265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1266     # . epilog
+1267     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1268     5d/pop-to-EBP
+1269     c3/return
+1270 
+1271 has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
+1272     # pseudocode:
+1273     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
+1274     #   curr = twig->end
+1275     #   while true
+1276     #     twig = next-token-from-slice(curr, word->end, '/')
+1277     #     if (twig.empty()) break
+1278     #     if (slice-equal?(twig, s)) return true
+1279     #     curr = twig->end
+1280     #   return false
+1281     # . prolog
+1282     55/push-EBP
+1283     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1284     # . save registers
+1285     51/push-ECX
+1286     52/push-EDX
+1287     56/push-ESI
+1288     57/push-EDI
+1289     # ESI = word
+1290     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+1291     # EDX = word->end
+1292     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
+1293     # var twig/EDI : (address slice) = {0, 0}
+1294     68/push  0/imm32/end
+1295     68/push  0/imm32/start
+1296     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+1297     # next-token-from-slice(word->start, word->end, '/', twig)
+1298     # . . push args
+1299     57/push-EDI
+1300     68/push  0x2f/imm32/slash
+1301     52/push-EDX
+1302     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+1303     # . . call
+1304     e8/call  next-token-from-slice/disp32
+1305     # . . discard args
+1306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+1307     # curr/ECX = twig->end
+1308     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+1309 $has-metadata?:loop:
+1310     # next-token-from-slice(curr, word->end, '/', twig)
+1311     # . . push args
+1312     57/push-EDI
+1313     68/push  0x2f/imm32/slash
+1314     52/push-EDX
+1315     51/push-ECX
+1316     # . . call
+1317     e8/call  next-token-from-slice/disp32
+1318     # . . discard args
+1319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+1320     # if (slice-empty?(twig)) return false
+1321     # . EAX = slice-empty?(twig)
+1322     # . . push args
+1323     57/push-EDI
+1324     # . . call
+1325     e8/call  slice-empty?/disp32
+1326     # . . discard args
+1327     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1328     # . if (EAX != 0) return false
+1329     3d/compare-EAX-and  0/imm32
+1330     75/jump-if-not-equal  $has-metadata?:false/disp8
+1331     # if (slice-equal?(twig, s)) return true
+1332     # . EAX = slice-equal?(twig, s)
+1333     # . . push args
+1334     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1335     57/push-EDI
+1336     # . . call
+1337     e8/call  slice-equal?/disp32
+1338     # . . discard args
+1339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1340     # . if (EAX != 0) return true
+1341     3d/compare-EAX-and  0/imm32
+1342     75/jump-if-not-equal  $has-metadata?:true/disp8
+1343     # curr = twig->end
+1344     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+1345     eb/jump  $has-metadata?:loop/disp8
+1346 $has-metadata?:true:
+1347     b8/copy-to-EAX  1/imm32/true
+1348     eb/jump  $has-metadata?:end/disp8
+1349 $has-metadata?:false:
+1350     b8/copy-to-EAX  0/imm32/false
+1351 $has-metadata?:end:
+1352     # . reclaim locals
+1353     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1354     # . restore registers
+1355     5f/pop-to-EDI
+1356     5e/pop-to-ESI
+1357     5a/pop-to-EDX
+1358     59/pop-to-ECX
+1359     # . epilog
+1360     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1361     5d/pop-to-EBP
+1362     c3/return
+1363 
+1364 test-has-metadata-true:
+1365     # . prolog
+1366     55/push-EBP
+1367     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1368     # (EAX..ECX) = "ab/imm32"
+1369     b8/copy-to-EAX  "ab/imm32"/imm32
+1370     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1371     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1372     05/add-to-EAX  4/imm32
+1373     # var in/ESI : (address slice) = {EAX, ECX}
+1374     51/push-ECX
+1375     50/push-EAX
+1376     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+1377     # EAX = has-metadata?(ESI, "imm32")
+1378     # . . push args
+1379     68/push  "imm32"/imm32
+1380     56/push-ESI
+1381     # . . call
+1382     e8/call  has-metadata?/disp32
+1383     # . . discard args
+1384     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1385     # check-ints-equal(EAX, 1, msg)
+1386     # . . push args
+1387     68/push  "F - test-has-metadata-true"/imm32
+1388     68/push  1/imm32/true
+1389     50/push-EAX
+1390     # . . call
+1391     e8/call  check-ints-equal/disp32
+1392     # . . discard args
+1393     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1394     # . epilog
+1395     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1396     5d/pop-to-EBP
+1397     c3/return
+1398 
+1399 test-has-metadata-false:
+1400     # . prolog
+1401     55/push-EBP
+1402     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1403     # (EAX..ECX) = "ab/c"
+1404     b8/copy-to-EAX  "ab/c"/imm32
+1405     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1406     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1407     05/add-to-EAX  4/imm32
+1408     # var in/ESI : (address slice) = {EAX, ECX}
+1409     51/push-ECX
+1410     50/push-EAX
+1411     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+1412     # EAX = has-metadata?(ESI, "d")
+1413     # . . push args
+1414     68/push  "d"/imm32
+1415     56/push-ESI
+1416     # . . call
+1417     e8/call  has-metadata?/disp32
+1418     # . . discard args
+1419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1420     # check-ints-equal(EAX, 0, msg)
+1421     # . . push args
+1422     68/push  "F - test-has-metadata-false"/imm32
+1423     68/push  0/imm32/false
+1424     50/push-EAX
+1425     # . . call
+1426     e8/call  check-ints-equal/disp32
+1427     # . . discard args
+1428     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1429     # . epilog
+1430     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1431     5d/pop-to-EBP
+1432     c3/return
+1433 
+1434 test-has-metadata-ignore-name:
+1435     # . prolog
+1436     55/push-EBP
+1437     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1438     # (EAX..ECX) = "a/b"
+1439     b8/copy-to-EAX  "a/b"/imm32
+1440     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1441     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1442     05/add-to-EAX  4/imm32
+1443     # var in/ESI : (address slice) = {EAX, ECX}
+1444     51/push-ECX
+1445     50/push-EAX
+1446     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+1447     # EAX = has-metadata?(ESI, "a")
+1448     # . . push args
+1449     68/push  "a"/imm32
+1450     56/push-ESI
+1451     # . . call
+1452     e8/call  has-metadata?/disp32
+1453     # . . discard args
+1454     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1455     # check-ints-equal(EAX, 0, msg)
+1456     # . . push args
+1457     68/push  "F - test-has-metadata-ignore-name"/imm32
+1458     68/push  0/imm32/false
+1459     50/push-EAX
+1460     # . . call
+1461     e8/call  check-ints-equal/disp32
+1462     # . . discard args
+1463     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1464     # . epilog
+1465     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1466     5d/pop-to-EBP
+1467     c3/return
+1468 
+1469 test-has-metadata-multiple-true:
+1470     # . prolog
+1471     55/push-EBP
+1472     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1473     # (EAX..ECX) = "a/b/c"
+1474     b8/copy-to-EAX  "a/b/c"/imm32
+1475     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1476     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1477     05/add-to-EAX  4/imm32
+1478     # var in/ESI : (address slice) = {EAX, ECX}
+1479     51/push-ECX
+1480     50/push-EAX
+1481     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+1482     # EAX = has-metadata?(ESI, "c")
+1483     # . . push args
+1484     68/push  "c"/imm32
+1485     56/push-ESI
+1486     # . . call
+1487     e8/call  has-metadata?/disp32
+1488     # . . discard args
+1489     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1490     # check-ints-equal(EAX, 1, msg)
+1491     # . . push args
+1492     68/push  "F - test-has-metadata-multiple-true"/imm32
+1493     68/push  1/imm32/true
+1494     50/push-EAX
+1495     # . . call
+1496     e8/call  check-ints-equal/disp32
+1497     # . . discard args
+1498     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1499     # . epilog
+1500     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1501     5d/pop-to-EBP
+1502     c3/return
+1503 
+1504 test-has-metadata-multiple-false:
+1505     # . prolog
+1506     55/push-EBP
+1507     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1508     # (EAX..ECX) = "a/b/c"
+1509     b8/copy-to-EAX  "a/b/c"/imm32
+1510     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1511     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1512     05/add-to-EAX  4/imm32
+1513     # var in/ESI : (address slice) = {EAX, ECX}
+1514     51/push-ECX
+1515     50/push-EAX
+1516     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+1517     # EAX = has-metadata?(ESI, "d")
+1518     # . . push args
+1519     68/push  "d"/imm32
+1520     56/push-ESI
+1521     # . . call
+1522     e8/call  has-metadata?/disp32
+1523     # . . discard args
+1524     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1525     # check-ints-equal(EAX, 0, msg)
+1526     # . . push args
+1527     68/push  "F - test-has-metadata-multiple-false"/imm32
+1528     68/push  0/imm32/false
+1529     50/push-EAX
+1530     # . . call
+1531     e8/call  check-ints-equal/disp32
+1532     # . . discard args
+1533     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1534     # . epilog
+1535     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1536     5d/pop-to-EBP
+1537     c3/return
+1538 
+1539 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
+1540 # it in 'width' bytes of hex, least significant first.
+1541 # Otherwise just print the entire word including metadata.
+1542 # Always print a trailing space.
+1543 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
+1544     # . prolog
+1545     55/push-EBP
+1546     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1547     # . save registers
+1548     50/push-EAX
+1549     56/push-ESI
+1550     57/push-EDI
+1551     # ESI = word
+1552     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+1553     # var name/EDI : (address slice) = {0, 0}
+1554     68/push  0/imm32/end
+1555     68/push  0/imm32/start
+1556     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+1557     # datum = next-token-from-slice(word->start, word->end, '/')
+1558     # . . push args
+1559     57/push-EDI
+1560     68/push  0x2f/imm32/slash
+1561     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+1562     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+1563     # . . call
+1564     e8/call  next-token-from-slice/disp32
+1565     # . . discard args
+1566     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+1567     # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return
+1568     # . EAX = is-valid-name?(name)
+1569     # . . push args
+1570     57/push-EDI
+1571     # . . call
+1572     e8/call  is-valid-name?/disp32
+1573     # . . discard args
+1574     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1575     # . if (EAX != 0)
+1576     3d/compare-EAX-and  0/imm32
+1577     74/jump-if-equal  $emit:hex-int/disp8
+1578 $emit:name:
+1579     # . write-slice-buffered(out, word)
+1580     # . . push args
+1581     56/push-ESI
+1582     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1583     # . . call
+1584     e8/call  write-slice-buffered/disp32
+1585     # . . discard args
+1586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1587     # . write-buffered(out, " ")
+1588     # . . push args
+1589     68/push  " "/imm32
+1590     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1591     # . . call
+1592     e8/call  write-buffered/disp32
+1593     # . . discard args
+1594     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1595     # . return
+1596     eb/jump  $emit:end/disp8
+1597     # otherwise emit-hex(out, parse-hex-int(datum), width)
+1598     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
+1599     #   name or a hex number, but we're only going to be passing in real legal
+1600     #   programs. We just want to make sure that valid names aren't treated as
+1601     #   (valid) hex numbers.)
+1602 $emit:hex-int:
+1603     # . value/EAX = parse-hex-int(datum)
+1604     # . . push args
+1605     57/push-EDI
+1606     # . . call
+1607     e8/call  parse-hex-int/disp32
+1608     # . . discard args
+1609     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1610     # . emit-hex(out, value, width)
+1611     # . . push args
+1612     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+1613     50/push-EAX
+1614     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1615     # . . call
+1616     e8/call  emit-hex/disp32
+1617     # . . discard args
+1618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1619 $emit:end:
+1620     # . reclaim locals
+1621     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1622     # . restore registers
+1623     5f/pop-to-EDI
+1624     5e/pop-to-ESI
+1625     58/pop-to-EAX
+1626     # . epilog
+1627     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1628     5d/pop-to-EBP
+1629     c3/return
+1630 
+1631 test-emit-number:
+1632     # . prolog
+1633     55/push-EBP
+1634     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1635     # setup
+1636     # . clear-stream(_test-output-stream)
+1637     # . . push args
+1638     68/push  _test-output-stream/imm32
+1639     # . . call
+1640     e8/call  clear-stream/disp32
+1641     # . . discard args
+1642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1643     # . clear-stream(_test-output-buffered-file+4)
+1644     # . . push args
+1645     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1646     05/add-to-EAX  4/imm32
+1647     50/push-EAX
+1648     # . . call
+1649     e8/call  clear-stream/disp32
+1650     # . . discard args
+1651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1652     # (EAX..ECX) = "30"
+1653     b8/copy-to-EAX  "30"/imm32
+1654     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1655     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1656     05/add-to-EAX  4/imm32
+1657     # var slice/ECX = {EAX, ECX}
+1658     51/push-ECX
+1659     50/push-EAX
+1660     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1661     # emit(_test-output-buffered-file, slice, 1)
+1662     # . . push args
+1663     68/push  1/imm32
+1664     51/push-ECX
+1665     68/push  _test-output-buffered-file/imm32
+1666     # . . call
+1667     e8/call  emit/disp32
+1668     # . . discard args
+1669     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1670     # flush(_test-output-buffered-file)
+1671     # . . push args
+1672     68/push  _test-output-buffered-file/imm32
+1673     # . . call
+1674     e8/call  flush/disp32
+1675     # . . discard args
+1676     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1677     # check-stream-equal(_test-output-stream, "30 ", msg)
+1678     # . . push args
+1679     68/push  "F - test-emit-number/1"/imm32
+1680     68/push  "30 "/imm32
+1681     68/push  _test-output-stream/imm32
+1682     # . . call
+1683     e8/call  check-stream-equal/disp32
+1684     # . . discard args
+1685     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1686     # . epilog
+1687     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1688     5d/pop-to-EBP
+1689     c3/return
+1690 
+1691 test-emit-negative-number:
+1692     # test support for sign-extending negative numbers
+1693     # . prolog
+1694     55/push-EBP
+1695     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1696     # setup
+1697     # . clear-stream(_test-output-stream)
+1698     # . . push args
+1699     68/push  _test-output-stream/imm32
+1700     # . . call
+1701     e8/call  clear-stream/disp32
+1702     # . . discard args
+1703     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1704     # . clear-stream(_test-output-buffered-file+4)
+1705     # . . push args
+1706     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1707     05/add-to-EAX  4/imm32
+1708     50/push-EAX
+1709     # . . call
+1710     e8/call  clear-stream/disp32
+1711     # . . discard args
+1712     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1713     # (EAX..ECX) = "-2"
+1714     b8/copy-to-EAX  "-2"/imm32
+1715     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1716     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1717     05/add-to-EAX  4/imm32
+1718     # var slice/ECX = {EAX, ECX}
+1719     51/push-ECX
+1720     50/push-EAX
+1721     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1722     # emit(_test-output-buffered-file, slice, 2)
+1723     # . . push args
+1724     68/push  2/imm32
+1725     51/push-ECX
+1726     68/push  _test-output-buffered-file/imm32
+1727     # . . call
+1728     e8/call  emit/disp32
+1729     # . . discard args
+1730     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1731     # flush(_test-output-buffered-file)
+1732     # . . push args
+1733     68/push  _test-output-buffered-file/imm32
+1734     # . . call
+1735     e8/call  flush/disp32
+1736     # . . discard args
+1737     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1738     # check-stream-equal(_test-output-stream, "fe ff ", msg)
+1739     # . . push args
+1740     68/push  "F - test-emit-number/1"/imm32
+1741     68/push  "fe ff "/imm32
+1742     68/push  _test-output-stream/imm32
+1743     # . . call
+1744     e8/call  check-stream-equal/disp32
+1745     # . . discard args
+1746     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1747     # . epilog
+1748     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1749     5d/pop-to-EBP
+1750     c3/return
+1751 
+1752 test-emit-number-with-metadata:
+1753     # . prolog
+1754     55/push-EBP
+1755     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1756     # setup
+1757     # . clear-stream(_test-output-stream)
+1758     # . . push args
+1759     68/push  _test-output-stream/imm32
+1760     # . . call
+1761     e8/call  clear-stream/disp32
+1762     # . . discard args
+1763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1764     # . clear-stream(_test-output-buffered-file+4)
+1765     # . . push args
+1766     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1767     05/add-to-EAX  4/imm32
+1768     50/push-EAX
+1769     # . . call
+1770     e8/call  clear-stream/disp32
+1771     # . . discard args
+1772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1773     # (EAX..ECX) = "-2/foo"
+1774     b8/copy-to-EAX  "-2/foo"/imm32
+1775     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1776     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1777     05/add-to-EAX  4/imm32
+1778     # var slice/ECX = {EAX, ECX}
+1779     51/push-ECX
+1780     50/push-EAX
+1781     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1782     # emit(_test-output-buffered-file, slice, 2)
+1783     # . . push args
+1784     68/push  2/imm32
+1785     51/push-ECX
+1786     68/push  _test-output-buffered-file/imm32
+1787     # . . call
+1788     e8/call  emit/disp32
+1789     # . . discard args
+1790     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1791     # flush(_test-output-buffered-file)
+1792     # . . push args
+1793     68/push  _test-output-buffered-file/imm32
+1794     # . . call
+1795     e8/call  flush/disp32
+1796     # . . discard args
+1797     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1798     # the '/foo' will have no impact on the output
+1799     # check-stream-equal(_test-output-stream, "fe ff ", msg)
+1800     # . . push args
+1801     68/push  "F - test-emit-number-with-metadata"/imm32
+1802     68/push  "fe ff "/imm32
+1803     68/push  _test-output-stream/imm32
+1804     # . . call
+1805     e8/call  check-stream-equal/disp32
+1806     # . . discard args
+1807     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1808     # . epilog
+1809     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1810     5d/pop-to-EBP
+1811     c3/return
+1812 
+1813 test-emit-non-number:
+1814     # . prolog
+1815     55/push-EBP
+1816     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1817     # setup
+1818     # . clear-stream(_test-output-stream)
+1819     # . . push args
+1820     68/push  _test-output-stream/imm32
+1821     # . . call
+1822     e8/call  clear-stream/disp32
+1823     # . . discard args
+1824     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1825     # . clear-stream(_test-output-buffered-file+4)
+1826     # . . push args
+1827     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1828     05/add-to-EAX  4/imm32
+1829     50/push-EAX
+1830     # . . call
+1831     e8/call  clear-stream/disp32
+1832     # . . discard args
+1833     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1834     # (EAX..ECX) = "xyz"
+1835     b8/copy-to-EAX  "xyz"/imm32
+1836     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1837     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1838     05/add-to-EAX  4/imm32
+1839     # var slice/ECX = {EAX, ECX}
+1840     51/push-ECX
+1841     50/push-EAX
+1842     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1843     # emit(_test-output-buffered-file, slice, 2)
+1844     # . . push args
+1845     68/push  2/imm32
+1846     51/push-ECX
+1847     68/push  _test-output-buffered-file/imm32
+1848     # . . call
+1849     e8/call  emit/disp32
+1850     # . . discard args
+1851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1852     # flush(_test-output-buffered-file)
+1853     # . . push args
+1854     68/push  _test-output-buffered-file/imm32
+1855     # . . call
+1856     e8/call  flush/disp32
+1857     # . . discard args
+1858     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1859     # check-stream-equal(_test-output-stream, "xyz", msg)
+1860     # . . push args
+1861     68/push  "F - test-emit-non-number"/imm32
+1862     68/push  "xyz "/imm32
+1863     68/push  _test-output-stream/imm32
+1864     # . . call
+1865     e8/call  check-stream-equal/disp32
+1866     # . . discard args
+1867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1868     # . epilog
+1869     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1870     5d/pop-to-EBP
+1871     c3/return
+1872 
+1873 test-emit-non-number-with-metadata:
+1874     # . prolog
+1875     55/push-EBP
+1876     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1877     # setup
+1878     # . clear-stream(_test-output-stream)
+1879     # . . push args
+1880     68/push  _test-output-stream/imm32
+1881     # . . call
+1882     e8/call  clear-stream/disp32
+1883     # . . discard args
+1884     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1885     # . clear-stream(_test-output-buffered-file+4)
+1886     # . . push args
+1887     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1888     05/add-to-EAX  4/imm32
+1889     50/push-EAX
+1890     # . . call
+1891     e8/call  clear-stream/disp32
+1892     # . . discard args
+1893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1894     # (EAX..ECX) = "xyz/"
+1895     b8/copy-to-EAX  "xyz/"/imm32
+1896     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1897     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1898     05/add-to-EAX  4/imm32
+1899     # var slice/ECX = {EAX, ECX}
+1900     51/push-ECX
+1901     50/push-EAX
+1902     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1903     # emit(_test-output-buffered-file, slice, 2)
+1904     # . . push args
+1905     68/push  2/imm32
+1906     51/push-ECX
+1907     68/push  _test-output-buffered-file/imm32
+1908     # . . call
+1909     e8/call  emit/disp32
+1910     # . . discard args
+1911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1912     # flush(_test-output-buffered-file)
+1913     # . . push args
+1914     68/push  _test-output-buffered-file/imm32
+1915     # . . call
+1916     e8/call  flush/disp32
+1917     # . . discard args
+1918     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1919     # check-stream-equal(_test-output-stream, "xyz/", msg)
+1920     # . . push args
+1921     68/push  "F - test-emit-non-number-with-metadata"/imm32
+1922     68/push  "xyz/ "/imm32
+1923     68/push  _test-output-stream/imm32
+1924     # . . call
+1925     e8/call  check-stream-equal/disp32
+1926     # . . discard args
+1927     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1928     # . epilog
+1929     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1930     5d/pop-to-EBP
+1931     c3/return
+1932 
+1933 test-emit-non-number-with-all-hex-digits-and-metadata:
+1934     # . prolog
+1935     55/push-EBP
+1936     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1937     # setup
+1938     # . clear-stream(_test-output-stream)
+1939     # . . push args
+1940     68/push  _test-output-stream/imm32
+1941     # . . call
+1942     e8/call  clear-stream/disp32
+1943     # . . discard args
+1944     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1945     # . clear-stream(_test-output-buffered-file+4)
+1946     # . . push args
+1947     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1948     05/add-to-EAX  4/imm32
+1949     50/push-EAX
+1950     # . . call
+1951     e8/call  clear-stream/disp32
+1952     # . . discard args
+1953     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1954     # (EAX..ECX) = "abcd/xyz"
+1955     b8/copy-to-EAX  "abcd/xyz"/imm32
+1956     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1957     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+1958     05/add-to-EAX  4/imm32
+1959     # var slice/ECX = {EAX, ECX}
+1960     51/push-ECX
+1961     50/push-EAX
+1962     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1963     # emit(_test-output-buffered-file, slice, 2)
+1964     # . . push args
+1965     68/push  2/imm32
+1966     51/push-ECX
+1967     68/push  _test-output-buffered-file/imm32
+1968     # . . call
+1969     e8/call  emit/disp32
+1970     # . . discard args
+1971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1972     # flush(_test-output-buffered-file)
+1973     # . . push args
+1974     68/push  _test-output-buffered-file/imm32
+1975     # . . call
+1976     e8/call  flush/disp32
+1977     # . . discard args
+1978     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1979 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+2005     # check-stream-equal(_test-output-stream, "abcd/xyz")
+2006     # . . push args
+2007     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
+2008     68/push  "abcd/xyz "/imm32
+2009     68/push  _test-output-stream/imm32
+2010     # . . call
+2011     e8/call  check-stream-equal/disp32
+2012     # . . discard args
+2013     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2014     # . epilog
+2015     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2016     5d/pop-to-EBP
+2017     c3/return
+2018 
+2019 # conditions for 'valid' names that are not at risk of looking like hex numbers
+2020 # keep in sync with the rules in labels.cc
+2021 #: - if it starts with a digit, it's treated as a number. If it can't be
+2022 #:   parsed as hex it will raise an error.
+2023 #: - if it starts with '-' it's treated as a number.
+2024 #: - if it starts with '0x' it's treated as a number. (redundant)
+2025 #: - if it's two characters long, it can't be a name. Either it's a hex
+2026 #:   byte, or it raises an error.
+2027 is-valid-name?:  # in : (address slice) -> EAX : boolean
+2028     # . prolog
+2029     55/push-EBP
+2030     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2031     # . save registers
+2032     51/push-ECX
+2033     56/push-ESI
+2034     # ESI = in
+2035     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+2036     # start/ECX = in->start
+2037     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+2038     # end/EAX = in->end
+2039     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+2040 $is-valid-name?:check0:
+2041     # if (start >= end) return false
+2042     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # compare ECX with EAX
+2043     73/jump-if-greater-or-equal-unsigned  $is-valid-name?:false/disp8
+2044 $is-valid-name?:check1:
+2045     # EAX -= ECX
+2046     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+2047     # if (EAX == 2) return false
+2048     3d/compare-EAX-and  2/imm32
+2049     74/jump-if-equal  $is-valid-name?:false/disp8
+2050 $is-valid-name?:check2:
+2051     # c/EAX = *ECX
+2052     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2053     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+2054     # if (c == "-") return false
+2055     3d/compare-EAX-and  2d/imm32/-
+2056     74/jump-if-equal  $is-valid-name?:false/disp8
+2057 $is-valid-name?:check3a:
+2058     # if (c < "0") return true
+2059     3d/compare-EAX-with  30/imm32/0
+2060     7c/jump-if-lesser  $is-valid-name?:true/disp8
+2061 $is-valid-name?:check3b:
+2062     # if (c > "9") return true
+2063     3d/compare-EAX-with  39/imm32/9
+2064     7f/jump-if-greater  $is-valid-name?:true/disp8
+2065 $is-valid-name?:false:
+2066     # return false
+2067     b8/copy-to-EAX  0/imm32/false
+2068     eb/jump  $is-valid-name?:end/disp8
+2069 $is-valid-name?:true:
+2070     # return true
+2071     b8/copy-to-EAX  1/imm32/true
+2072 $is-valid-name?:end:
+2073     # . restore registers
+2074     5e/pop-to-ESI
+2075     59/pop-to-ECX
+2076     # . epilog
+2077     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2078     5d/pop-to-EBP
+2079     c3/return
+2080 
+2081 test-is-valid-name-digit-prefix:
+2082     # . prolog
+2083     55/push-EBP
+2084     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2085     # (EAX..ECX) = "34"
+2086     b8/copy-to-EAX  "34"/imm32
+2087     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2088     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2089     05/add-to-EAX  4/imm32
+2090     # var slice/ECX = {EAX, ECX}
+2091     51/push-ECX
+2092     50/push-EAX
+2093     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2094     # EAX = is-valid-name?(slice)
+2095     # . . push args
+2096     51/push-ECX
+2097     # . . call
+2098     e8/call  is-valid-name?/disp32
+2099     # . . discard args
+2100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2101     # check-ints-equal(EAX, 0, msg)
+2102     # . . push args
+2103     68/push  "F - test-is-valid-name-digit-prefix"/imm32
+2104     68/push  0/imm32/false
+2105     50/push-EAX
+2106     # . . call
+2107     e8/call  check-ints-equal/disp32
+2108     # . . discard args
+2109     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2110     # . epilog
+2111     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2112     5d/pop-to-EBP
+2113     c3/return
+2114 
+2115 test-is-valid-name-negative-prefix:
+2116     # . prolog
+2117     55/push-EBP
+2118     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2119     # (EAX..ECX) = "-0x34"
+2120     b8/copy-to-EAX  "-0x34"/imm32
+2121     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2122     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2123     05/add-to-EAX  4/imm32
+2124     # var slice/ECX = {EAX, ECX}
+2125     51/push-ECX
+2126     50/push-EAX
+2127     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2128     # EAX = is-valid-name?(slice)
+2129     # . . push args
+2130     51/push-ECX
+2131     # . . call
+2132     e8/call  is-valid-name?/disp32
+2133     # . . discard args
+2134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2135     # check-ints-equal(EAX, 0, msg)
+2136     # . . push args
+2137     68/push  "F - test-is-valid-name-negative-prefix"/imm32
+2138     68/push  0/imm32/false
+2139     50/push-EAX
+2140     # . . call
+2141     e8/call  check-ints-equal/disp32
+2142     # . . discard args
+2143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2144     # . epilog
+2145     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2146     5d/pop-to-EBP
+2147     c3/return
+2148 
+2149 test-is-valid-name-0x-prefix:
+2150     # . prolog
+2151     55/push-EBP
+2152     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2153     # (EAX..ECX) = "0x34"
+2154     b8/copy-to-EAX  "0x34"/imm32
+2155     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2156     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2157     05/add-to-EAX  4/imm32
+2158     # var slice/ECX = {EAX, ECX}
+2159     51/push-ECX
+2160     50/push-EAX
+2161     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2162     # EAX = is-valid-name?(slice)
+2163     # . . push args
+2164     51/push-ECX
+2165     # . . call
+2166     e8/call  is-valid-name?/disp32
+2167     # . . discard args
+2168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2169     # check-ints-equal(EAX, 0, msg)
+2170     # . . push args
+2171     68/push  "F - test-is-valid-name-0x-prefix"/imm32
+2172     68/push  0/imm32/false
+2173     50/push-EAX
+2174     # . . call
+2175     e8/call  check-ints-equal/disp32
+2176     # . . discard args
+2177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2178     # . epilog
+2179     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2180     5d/pop-to-EBP
+2181     c3/return
+2182 
+2183 test-is-valid-name-starts-with-pre-digit:
+2184     # . prolog
+2185     55/push-EBP
+2186     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2187     # (EAX..ECX) = "/03"
+2188     b8/copy-to-EAX  "/03"/imm32
+2189     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2190     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2191     05/add-to-EAX  4/imm32
+2192     # var slice/ECX = {EAX, ECX}
+2193     51/push-ECX
+2194     50/push-EAX
+2195     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2196     # EAX = is-valid-name?(slice)
+2197     # . . push args
+2198     51/push-ECX
+2199     # . . call
+2200     e8/call  is-valid-name?/disp32
+2201     # . . discard args
+2202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2203     # check-ints-equal(EAX, 1, msg)
+2204     # . . push args
+2205     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
+2206     68/push  1/imm32/true
+2207     50/push-EAX
+2208     # . . call
+2209     e8/call  check-ints-equal/disp32
+2210     # . . discard args
+2211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2212     # . epilog
+2213     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2214     5d/pop-to-EBP
+2215     c3/return
+2216 
+2217 test-is-valid-name-starts-with-post-digit:
+2218     # . prolog
+2219     55/push-EBP
+2220     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2221     # (EAX..ECX) = "q34"
+2222     b8/copy-to-EAX  "q34"/imm32
+2223     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2224     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2225     05/add-to-EAX  4/imm32
+2226     # var slice/ECX = {EAX, ECX}
+2227     51/push-ECX
+2228     50/push-EAX
+2229     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2230     # EAX = is-valid-name?(slice)
+2231     # . . push args
+2232     51/push-ECX
+2233     # . . call
+2234     e8/call  is-valid-name?/disp32
+2235     # . . discard args
+2236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2237     # check-ints-equal(EAX, 1, msg)
+2238     # . . push args
+2239     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
+2240     68/push  1/imm32/true
+2241     50/push-EAX
+2242     # . . call
+2243     e8/call  check-ints-equal/disp32
+2244     # . . discard args
+2245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2246     # . epilog
+2247     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2248     5d/pop-to-EBP
+2249     c3/return
+2250 
+2251 test-is-valid-name-starts-with-digit:
+2252     # . prolog
+2253     55/push-EBP
+2254     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2255     # (EAX..ECX) = "0x34"
+2256     b8/copy-to-EAX  "0x34"/imm32
+2257     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2258     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2259     05/add-to-EAX  4/imm32
+2260     # var slice/ECX = {EAX, ECX}
+2261     51/push-ECX
+2262     50/push-EAX
+2263     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2264     # EAX = is-valid-name?(slice)
+2265     # . . push args
+2266     51/push-ECX
+2267     # . . call
+2268     e8/call  is-valid-name?/disp32
+2269     # . . discard args
+2270     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2271     # check-ints-equal(EAX, 0, msg)
+2272     # . . push args
+2273     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
+2274     68/push  0/imm32/false
+2275     50/push-EAX
+2276     # . . call
+2277     e8/call  check-ints-equal/disp32
+2278     # . . discard args
+2279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2280     # . epilog
+2281     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2282     5d/pop-to-EBP
+2283     c3/return
+2284 
+2285 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
+2286 emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
+2287     # . prolog
+2288     55/push-EBP
+2289     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2290     # . save registers
+2291     50/push-EAX
+2292     51/push-ECX
+2293     52/push-EDX
+2294     53/push-EBX
+2295     57/push-EDI
+2296     # EDI = out
+2297     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+2298     # EBX = n
+2299     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
+2300     # EDX = width
+2301     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
+2302     # var curr/ECX = 0
+2303     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+2304 $emit-hex:loop:
+2305     # if (curr >= width) break
+2306     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+2307     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
+2308     # print-byte-buffered(out, EBX)
+2309     # . . push args
+2310     53/push-EBX
+2311     57/push-EDI
+2312     # . . call
+2313     e8/call  print-byte-buffered/disp32
+2314     # . . discard args
+2315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2316     # write-byte-buffered(out, ' ')
+2317     # . . push args
+2318     68/push  0x20/imm32/space
+2319     57/push-EDI
+2320     # . . call
+2321     e8/call  write-byte-buffered/disp32
+2322     # . . discard args
+2323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2324     # EBX = EBX >> 8
+2325     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
+2326 $emit-hex:continue:
+2327     # ++curr
+2328     41/increment-ECX
+2329     eb/jump  $emit-hex:loop/disp8
+2330 $emit-hex:end:
+2331     # . restore registers
+2332     5f/pop-to-EDI
+2333     5b/pop-to-EBX
+2334     5a/pop-to-EDX
+2335     59/pop-to-ECX
+2336     58/pop-to-EAX
+2337     # . epilog
+2338     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2339     5d/pop-to-EBP
+2340     c3/return
+2341 
+2342 test-emit-hex-single-byte:
+2343     # setup
+2344     # . clear-stream(_test-output-stream)
+2345     # . . push args
+2346     68/push  _test-output-stream/imm32
+2347     # . . call
+2348     e8/call  clear-stream/disp32
+2349     # . . discard args
+2350     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2351     # . clear-stream(_test-output-buffered-file+4)
+2352     # . . push args
+2353     b8/copy-to-EAX  _test-output-buffered-file/imm32
+2354     05/add-to-EAX  4/imm32
+2355     50/push-EAX
+2356     # . . call
+2357     e8/call  clear-stream/disp32
+2358     # . . discard args
+2359     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2360     # emit-hex(_test-output-buffered-file, 0xab, 1)
+2361     # . . push args
+2362     68/push  1/imm32
+2363     68/push  0xab/imm32
+2364     68/push  _test-output-buffered-file/imm32
+2365     # . . call
+2366     e8/call  emit-hex/disp32
+2367     # . . discard args
+2368     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2369     # flush(_test-output-buffered-file)
+2370     # . . push args
+2371     68/push  _test-output-buffered-file/imm32
+2372     # . . call
+2373     e8/call  flush/disp32
+2374     # . . discard args
+2375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2376     # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
+2377     # . . push args
+2378     68/push  "F - test-emit-hex-single-byte"/imm32
+2379     68/push  0x206261/imm32
+2380     # . . push *_test-output-stream->data
+2381     b8/copy-to-EAX  _test-output-stream/imm32
+2382     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+2383     # . . call
+2384     e8/call  check-ints-equal/disp32
+2385     # . . discard args
+2386     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2387     # . end
+2388     c3/return
+2389 
+2390 test-emit-hex-multiple-byte:
+2391     # setup
+2392     # . clear-stream(_test-output-stream)
+2393     # . . push args
+2394     68/push  _test-output-stream/imm32
+2395     # . . call
+2396     e8/call  clear-stream/disp32
+2397     # . . discard args
+2398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2399     # . clear-stream(_test-output-buffered-file+4)
+2400     # . . push args
+2401     b8/copy-to-EAX  _test-output-buffered-file/imm32
+2402     05/add-to-EAX  4/imm32
+2403     50/push-EAX
+2404     # . . call
+2405     e8/call  clear-stream/disp32
+2406     # . . discard args
+2407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2408     # emit-hex(_test-output-buffered-file, 0x1234, 2)
+2409     # . . push args
+2410     68/push  2/imm32
+2411     68/push  0x1234/imm32
+2412     68/push  _test-output-buffered-file/imm32
+2413     # . . call
+2414     e8/call  emit-hex/disp32
+2415     # . . discard args
+2416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2417     # flush(_test-output-buffered-file)
+2418     # . . push args
+2419     68/push  _test-output-buffered-file/imm32
+2420     # . . call
+2421     e8/call  flush/disp32
+2422     # . . discard args
+2423     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2424     # check-stream-equal(_test-output-stream, "34 12 ", msg)
+2425     # . . push args
+2426     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
+2427     68/push  "34 12 "/imm32
+2428     68/push  _test-output-stream/imm32
+2429     # . . call
+2430     e8/call  check-stream-equal/disp32
+2431     # . . discard args
+2432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2433     # . end
+2434     c3/return
+2435 
+2436 test-emit-hex-zero-pad:
+2437     # setup
+2438     # . clear-stream(_test-output-stream)
+2439     # . . push args
+2440     68/push  _test-output-stream/imm32
+2441     # . . call
+2442     e8/call  clear-stream/disp32
+2443     # . . discard args
+2444     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2445     # . clear-stream(_test-output-buffered-file+4)
+2446     # . . push args
+2447     b8/copy-to-EAX  _test-output-buffered-file/imm32
+2448     05/add-to-EAX  4/imm32
+2449     50/push-EAX
+2450     # . . call
+2451     e8/call  clear-stream/disp32
+2452     # . . discard args
+2453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2454     # emit-hex(_test-output-buffered-file, 0xab, 2)
+2455     # . . push args
+2456     68/push  2/imm32
+2457     68/push  0xab/imm32
+2458     68/push  _test-output-buffered-file/imm32
+2459     # . . call
+2460     e8/call  emit-hex/disp32
+2461     # . . discard args
+2462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2463     # flush(_test-output-buffered-file)
+2464     # . . push args
+2465     68/push  _test-output-buffered-file/imm32
+2466     # . . call
+2467     e8/call  flush/disp32
+2468     # . . discard args
+2469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2470     # check(_test-output-stream->data == 'ab 00 ')
+2471     # . . push args
+2472     68/push  "F - test-emit-hex-zero-pad/1"/imm32
+2473     68/push  "ab 00 "/imm32
+2474     68/push  _test-output-stream/imm32
+2475     # . . call
+2476     e8/call  check-stream-equal/disp32
+2477     # . . discard args
+2478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2479     # . end
+2480     c3/return
+2481 
+2482 test-emit-hex-negative:
+2483     # setup
+2484     # . clear-stream(_test-output-stream)
+2485     # . . push args
+2486     68/push  _test-output-stream/imm32
+2487     # . . call
+2488     e8/call  clear-stream/disp32
+2489     # . . discard args
+2490     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2491     # . clear-stream(_test-output-buffered-file+4)
+2492     # . . push args
+2493     b8/copy-to-EAX  _test-output-buffered-file/imm32
+2494     05/add-to-EAX  4/imm32
+2495     50/push-EAX
+2496     # . . call
+2497     e8/call  clear-stream/disp32
+2498     # . . discard args
+2499     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2500     # emit-hex(_test-output-buffered-file, -1, 2)
+2501     # . . push args
+2502     68/push  2/imm32
+2503     68/push  -1/imm32
+2504     68/push  _test-output-buffered-file/imm32
+2505     # . . call
+2506     e8/call  emit-hex/disp32
+2507     # . . discard args
+2508     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2509     # flush(_test-output-buffered-file)
+2510     # . . push args
+2511     68/push  _test-output-buffered-file/imm32
+2512     # . . call
+2513     e8/call  flush/disp32
+2514     # . . discard args
+2515     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2516     # check-stream-equal(_test-output-stream == "ff ff ")
+2517     # . . push args
+2518     68/push  "F - test-emit-hex-negative/1"/imm32
+2519     68/push  "ff ff "/imm32
+2520     68/push  _test-output-stream/imm32
+2521     # . . call
+2522     e8/call  check-stream-equal/disp32
+2523     # . . discard args
+2524     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2525     # . end
+2526     c3/return
+2527 
+2528 # print 'arr' in hex with a space after every byte
+2529 emit-hex-array:  # out : (address buffered-file), arr : (address array byte) -> <void>
+2530     # . prolog
+2531     55/push-EBP
+2532     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2533     # . save registers
+2534     50/push-EAX
+2535     51/push-ECX
+2536     52/push-EDX
+2537     57/push-EDI
+2538     # EDI = out
+2539     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+2540     # EDX = arr  # <== 0xbdffffe4
+2541     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+2542     # curr/ECX = arr->data
+2543     8d/copy-address                 1/mod/*+disp8   2/rm32/EDX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EDX+4 to ECX
+2544     # max/EDX = arr->data + arr->length
+2545     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # copy *EDX to EDX
+2546     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           1/r32/ECX   .               .                 # add ECX to EDX
+2547     # EAX = 0
+2548     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2549 $emit-hex-array:loop:
+2550     # if (curr >= width) break
+2551     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+2552     73/jump-if-greater-or-equal-unsigned  $emit-hex-array:end/disp8
+2553     # emit-hex(out, *curr, width=1)
+2554     # . . push args
+2555     68/push  1/imm32/width
+2556     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+2557     50/push-EAX
+2558     57/push-EDI
+2559     # . . call
+2560     e8/call  emit-hex/disp32
+2561     # . . discard args
+2562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2563     # ++curr
+2564     41/increment-ECX
+2565     eb/jump  $emit-hex-array:loop/disp8
+2566 $emit-hex-array:end:
+2567     # . restore registers
+2568     5f/pop-to-EDI
+2569     5a/pop-to-EDX
+2570     59/pop-to-ECX
+2571     58/pop-to-EAX
+2572     # . epilog
+2573     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2574     5d/pop-to-EBP
+2575     c3/return
+2576 
+2577 test-emit-hex-array:
+2578     # . prolog
+2579     55/push-EBP
+2580     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2581     # setup
+2582     # . clear-stream(_test-output-stream)
+2583     # . . push args
+2584     68/push  _test-output-stream/imm32
+2585     # . . call
+2586     e8/call  clear-stream/disp32
+2587     # . . discard args
+2588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2589     # . clear-stream(_test-output-buffered-file+4)
+2590     # . . push args
+2591     b8/copy-to-EAX  _test-output-buffered-file/imm32
+2592     05/add-to-EAX  4/imm32
+2593     50/push-EAX
+2594     # . . call
+2595     e8/call  clear-stream/disp32
+2596     # . . discard args
+2597     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2598     # var arr/ECX (address array byte) = [01, 02, 03]
+2599     68/push  0x00030201/imm32  # bytes 01 02 03
+2600     68/push  3/imm32/length
+2601     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2602     # emit-hex-array(_test-output-buffered-file, arr)
+2603     # . . push args
+2604     51/push-ECX
+2605     68/push  _test-output-buffered-file/imm32
+2606     # . . call
+2607     e8/call  emit-hex-array/disp32
+2608     # . . discard args
+2609     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2610     # . flush(_test-output-buffered-file)
+2611     # . . push args
+2612     68/push  _test-output-buffered-file/imm32
+2613     # . . call
+2614     e8/call  flush/disp32
+2615     # . . discard args
+2616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2617 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+2650     # check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg)
+2651     # . . push args
+2652     68/push  "F - test-emit-hex-array"/imm32
+2653     68/push  "01 02 03 "/imm32
+2654     68/push  _test-output-stream/imm32
+2655     # . . call
+2656     e8/call  check-next-stream-line-equal/disp32
+2657     # . . discard args
+2658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2659     # . epilog
+2660     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2661     5d/pop-to-EBP
+2662     c3/return
+2663 
+2664 compute-width: # word : (address array byte) -> EAX : int
+2665     # . prolog
+2666     55/push-EBP
+2667     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2668     # . save registers
+2669     51/push-ECX
+2670     # EAX = word
+2671     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to ECX
+2672     # ECX = word + word->length
+2673     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2674     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2675     # EAX = word->data
+2676     05/add-to-EAX  4/imm32
+2677     # var in/ECX : (address slice) = {EAX, ECX}
+2678     51/push-ECX
+2679     50/push-EAX
+2680     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2681     # return compute-width-of-slice(ECX)
+2682     # . . push args
+2683     51/push-ECX
+2684     # . . call
+2685     e8/call  compute-width-of-slice/disp32
+2686     # . . discard args
+2687     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2688 $compute-width:end:
+2689     # . reclaim locals
+2690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2691     # . restore registers
+2692     59/pop-to-ECX
+2693     # . epilog
+2694     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2695     5d/pop-to-EBP
+2696     c3/return
+2697 
+2698 compute-width-of-slice: # s : (address slice) -> EAX : int
+2699     # . prolog
+2700     55/push-EBP
+2701     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2702     # . save registers
+2703     51/push-ECX
+2704     # ECX = s
+2705     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+2706     # if (has-metadata?(word, "imm32")) return 4
+2707     # . EAX = has-metadata?(word, "imm32")
+2708     # . . push args
+2709     68/push  "imm32"/imm32
+2710     51/push-ECX
+2711     # . . call
+2712     e8/call  has-metadata?/disp32
+2713     # . . discard args
+2714     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2715     # . if (EAX != 0) return 4
+2716     3d/compare-EAX-and  0/imm32
+2717     b8/copy-to-EAX  4/imm32         # ZF is set, so we can overwrite EAX now
+2718     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
+2719     # if (has-metadata?(word, "disp32")) return 4
+2720     # . EAX = has-metadata?(word, "disp32")
+2721     # . . push args
+2722     68/push  "disp32"/imm32
+2723     51/push-ECX
+2724     # . . call
+2725     e8/call  has-metadata?/disp32
+2726     # . . discard args
+2727     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2728     # . if (EAX != 0) return 4
+2729     3d/compare-EAX-and  0/imm32
+2730     b8/copy-to-EAX  4/imm32         # ZF is set, so we can overwrite EAX now
+2731     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
+2732     # if (has-metadata?(word, "imm16")) return 2
+2733     # . EAX = has-metadata?(word, "imm16")
+2734     # . . push args
+2735     68/push  "imm16"/imm32
+2736     51/push-ECX
+2737     # . . call
+2738     e8/call  has-metadata?/disp32
+2739     # . . discard args
+2740     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2741     # . if (EAX != 0) return 2
+2742     3d/compare-EAX-and  0/imm32
+2743     b8/copy-to-EAX  2/imm32         # ZF is set, so we can overwrite EAX now
+2744     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
+2745     # if (has-metadata?(word, "disp16")) return 2
+2746     # . EAX = has-metadata?(word, "disp16")
+2747     # . . push args
+2748     68/push  "disp16"/imm32
+2749     51/push-ECX
+2750     # . . call
+2751     e8/call  has-metadata?/disp32
+2752     # . . discard args
+2753     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2754     # . if (EAX != 0) return 2
+2755     3d/compare-EAX-and  0/imm32
+2756     b8/copy-to-EAX  2/imm32         # ZF is set, so we can overwrite EAX now
+2757     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
+2758     # otherwise return 1
+2759     b8/copy-to-EAX  1/imm32
+2760 $compute-width-of-slice:end:
+2761     # . restore registers
+2762     59/pop-to-ECX
+2763     # . epilog
+2764     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2765     5d/pop-to-EBP
+2766     c3/return
+2767 
+2768 test-compute-width:
+2769     # . prolog
+2770     55/push-EBP
+2771     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2772 $test-compute-width:imm8:
+2773     # EAX = compute-width("0x2/imm8")
+2774     # . . push args
+2775     68/push  "0x2/imm8"/imm32
+2776     # . . call
+2777     e8/call  compute-width/disp32
+2778     # . . discard args
+2779     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2780     # check-ints-equal(EAX, 1, msg)
+2781     # . . push args
+2782     68/push  "F - test-compute-width: 0x2/imm8"/imm32
+2783     50/push-EAX
+2784     68/push  1/imm32
+2785     # . . call
+2786     e8/call  check-ints-equal/disp32
+2787     # . . discard args
+2788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2789 $test-compute-width:imm16:
+2790     # EAX = compute-width("4/imm16")
+2791     # . . push args
+2792     68/push  "4/imm16"/imm32
+2793     # . . call
+2794     e8/call  compute-width/disp32
+2795     # . . discard args
+2796     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2797     # check-ints-equal(EAX, 2, msg)
+2798     # . . push args
+2799     68/push  "F - test-compute-width: 4/imm16"/imm32
+2800     50/push-EAX
+2801     68/push  2/imm32
+2802     # . . call
+2803     e8/call  check-ints-equal/disp32
+2804     # . . discard args
+2805     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2806 $test-compute-width:imm32:
+2807     # EAX = compute-width("4/imm32")
+2808     # . . push args
+2809     68/push  "4/imm32"/imm32
+2810     # . . call
+2811     e8/call  compute-width/disp32
+2812     # . . discard args
+2813     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2814     # check-ints-equal(EAX, 4, msg)
+2815     # . . push args
+2816     68/push  "F - test-compute-width: 4/imm32"/imm32
+2817     50/push-EAX
+2818     68/push  4/imm32
+2819     # . . call
+2820     e8/call  check-ints-equal/disp32
+2821     # . . discard args
+2822     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2823 $test-compute-width:disp8:
+2824     # EAX = compute-width("foo/disp8")
+2825     # . . push args
+2826     68/push  "foo/disp8"/imm32
+2827     # . . call
+2828     e8/call  compute-width/disp32
+2829     # . . discard args
+2830     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2831     # check-ints-equal(EAX, 1, msg)
+2832     # . . push args
+2833     68/push  "F - test-compute-width: foo/disp8"/imm32
+2834     50/push-EAX
+2835     68/push  1/imm32
+2836     # . . call
+2837     e8/call  check-ints-equal/disp32
+2838     # . . discard args
+2839     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2840 $test-compute-width:disp16:
+2841     # EAX = compute-width("foo/disp16")
+2842     # . . push args
+2843     68/push  "foo/disp16"/imm32
+2844     # . . call
+2845     e8/call  compute-width/disp32
+2846     # . . discard args
+2847     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2848     # check-ints-equal(EAX, 2, msg)
+2849     # . . push args
+2850     68/push  "F - test-compute-width: foo/disp16"/imm32
+2851     50/push-EAX
+2852     68/push  2/imm32
+2853     # . . call
+2854     e8/call  check-ints-equal/disp32
+2855     # . . discard args
+2856     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2857 $test-compute-width:disp32:
+2858     # EAX = compute-width("foo/disp32")
+2859     # . . push args
+2860     68/push  "foo/disp32"/imm32
+2861     # . . call
+2862     e8/call  compute-width/disp32
+2863     # . . discard args
+2864     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2865     # check-ints-equal(EAX, 4, msg)
+2866     # . . push args
+2867     68/push  "F - test-compute-width: foo/disp32"/imm32
+2868     50/push-EAX
+2869     68/push  4/imm32
+2870     # . . call
+2871     e8/call  check-ints-equal/disp32
+2872     # . . discard args
+2873     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2874 $test-compute-width:no-metadata:
+2875     # EAX = compute-width("45")
+2876     # . . push args
+2877     68/push  "45"/imm32
+2878     # . . call
+2879     e8/call  compute-width/disp32
+2880     # . . discard args
+2881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2882     # check-ints-equal(EAX, 1, msg)
+2883     # . . push args
+2884     68/push  "F - test-compute-width: 45 (no metadata)"/imm32
+2885     50/push-EAX
+2886     68/push  1/imm32
+2887     # . . call
+2888     e8/call  check-ints-equal/disp32
+2889     # . . discard args
+2890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2891     # . epilog
+2892     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2893     5d/pop-to-EBP
+2894     c3/return
+2895 
+2896 is-label?: # word : (address slice) -> EAX : boolean
+2897     # . prolog
+2898     55/push-EBP
+2899     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2900     # . save registers
+2901     51/push-ECX
+2902     # ECX = word
+2903     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+2904     # ECX = word->end
+2905     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ECX+4) to ECX
+2906     # return *(word->end - 1) == ':'
+2907     # . EAX = 0
+2908     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2909     # . EAX = *((char *) word->end - 1)
+2910     8a/copy-byte                    1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/AL    -1/disp8         .                 # copy byte at *(ECX-1) to AL
+2911     # . return (EAX == ':')
+2912     3d/compare-EAX-and  0x3a/imm32/colon
+2913     b8/copy-to-EAX  1/imm32/true
+2914     74/jump-if-equal  $is-label?:end/disp8
+2915     b8/copy-to-EAX  0/imm32/false
+2916 $is-label?:end:
+2917     # . restore registers
+2918     59/pop-to-ECX
+2919     # . epilog
+2920     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2921     5d/pop-to-EBP
+2922     c3/return
+2923 
+2924 test-is-label?:
+2925     # . prolog
+2926     55/push-EBP
+2927     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2928 $test-is-label?:true:
+2929     # (EAX..ECX) = "AAA:"
+2930     b8/copy-to-EAX  "AAA:"/imm32
+2931     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2932     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2933     05/add-to-EAX  4/imm32
+2934     # var slice/ECX = {EAX, ECX}
+2935     51/push-ECX
+2936     50/push-EAX
+2937     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2938     # is-label?(slice/ECX)
+2939     # . . push args
+2940     51/push-ECX
+2941     # . . call
+2942     e8/call  is-label?/disp32
+2943     # . . discard args
+2944     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2945     # check-ints-equal(EAX, 1, msg)
+2946     # . . push args
+2947     68/push  "F - test-is-label?:true"/imm32
+2948     68/push  1/imm32
+2949     50/push-EAX
+2950     # . . call
+2951     e8/call  check-ints-equal/disp32
+2952     # . . discard args
+2953     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2954 $test-is-label?:false:
+2955     # (EAX..ECX) = "AAA"
+2956     b8/copy-to-EAX  "AAA"/imm32
+2957     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2958     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+2959     05/add-to-EAX  4/imm32
+2960     # var slice/ECX = {EAX, ECX}
+2961     51/push-ECX
+2962     50/push-EAX
+2963     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2964     # is-label?(slice/ECX)
+2965     # . . push args
+2966     51/push-ECX
+2967     # . . call
+2968     e8/call  is-label?/disp32
+2969     # . . discard args
+2970     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2971     # check-ints-equal(EAX, 0, msg)
+2972     # . . push args
+2973     68/push  "F - test-is-label?:false"/imm32
+2974     68/push  0/imm32
+2975     50/push-EAX
+2976     # . . call
+2977     e8/call  check-ints-equal/disp32
+2978     # . . discard args
+2979     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2980     # . epilog
+2981     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2982     5d/pop-to-EBP
+2983     c3/return
+2984 
+2985 == data
+2986 
+2987 _test-input-stream:
+2988     # current write index
+2989     0/imm32
+2990     # current read index
+2991     0/imm32
+2992     # length
+2993     0x100/imm32  # 256 bytes
+2994     # data (16 lines x 16 bytes/line)
+2995     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2996     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2997     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2998     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2999     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3000     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3001     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3002     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3003     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3004     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3005     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3006     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3007     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3008     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3009     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3011 
+3012 # a test buffered file for _test-input-stream
+3013 _test-input-buffered-file:
+3014     # file descriptor or (address stream)
+3015     _test-input-stream/imm32
+3016     # current write index
+3017     0/imm32
+3018     # current read index
+3019     0/imm32
+3020     # length
+3021     6/imm32
+3022     # data
+3023     00 00 00 00 00 00  # 6 bytes
+3024 
+3025 _test-output-stream:
+3026     # current write index
+3027     0/imm32
+3028     # current read index
+3029     0/imm32
+3030     # length
+3031     0x200/imm32  # 512 bytes
+3032     # data (32 lines x 16 bytes/line)
+3033     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3034     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3035     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3036     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3037     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3038     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3039     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3040     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3041     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3042     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3043     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3044     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3045     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3046     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3047     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3048     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3049     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3050     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3051     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3052     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3053     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3054     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3055     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3056     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3057     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3058     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3059     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3060     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3061     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3062     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3063     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3064     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3065 
+3066 # a test buffered file for _test-output-stream
+3067 _test-output-buffered-file:
+3068     # file descriptor or (address stream)
+3069     _test-output-stream/imm32
+3070     # current write index
+3071     0/imm32
+3072     # current read index
+3073     0/imm32
+3074     # length
+3075     6/imm32
+3076     # data
+3077     00 00 00 00 00 00  # 6 bytes
+3078 
+3079 _test-data-segment:
+3080   64/d 61/a 74/t 61/a
+3081 _test-data-segment-end:
+3082 
+3083 # . . vim:nowrap:textwidth=0
 
diff --git a/html/subx/apps/survey.subx.html b/html/subx/apps/survey.subx.html new file mode 100644 index 00000000..f43c3013 --- /dev/null +++ b/html/subx/apps/survey.subx.html @@ -0,0 +1,3188 @@ + + + + +Mu - subx/apps/survey.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/apps/survey.subx +
+   1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
+   2 # (landscape).
+   3 # Use the addresses assigned to:
+   4 #   a) replace labels
+   5 #   b) add segment headers with addresses and offsets correctly filled in
+   6 #
+   7 # To build (from the subx/ directory):
+   8 #   $ ./subx translate *.subx apps/survey.subx -o apps/survey
+   9 #
+  10 # The expected input is a stream of bytes with segment headers, comments and
+  11 # some interspersed labels.
+  12 #   $ cat x
+  13 #   == code 0x1
+  14 #   l1:
+  15 #   aa bb l1/imm8
+  16 #   cc dd l2/disp32
+  17 #   l2:
+  18 #   ee foo/imm32
+  19 #   == data 0x10
+  20 #   foo:
+  21 #     00
+  22 #
+  23 # The output is the stream of bytes without segment headers or label definitions,
+  24 # and with label references replaced with numeric values/displacements.
+  25 #
+  26 #   $ cat x  |./subx run apps/assort
+  27 #   ...ELF header bytes...
+  28 #   # ELF header above will specify that code segment begins at this offset
+  29 #   aa bb nn  # some computed address
+  30 #   cc dd nn nn nn nn  # some computed displacement
+  31 #   ee nn nn nn nn  # some computed address
+  32 #   # ELF header above will specify that data segment begins at this offset
+  33 #   00
+  34 
+  35 == code
+  36 #   instruction                     effective address                                                   register    displacement    immediate
+  37 # . op          subop               mod             rm32          base        index         scale       r32
+  38 # . 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
+  39 
+  40 Entry:
+  41     # Heap = new-segment(64KB)
+  42     # . . push args
+  43     68/push  Heap/imm32
+  44     68/push  0x10000/imm32/64KB
+  45     # . . call
+  46     e8/call  new-segment/disp32
+  47     # . . discard args
+  48     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  49     # initialize-trace-stream(256KB)
+  50     # . . push args
+  51     68/push  0x40000/imm32/256KB
+  52     # . . call
+  53     e8/call  initialize-trace-stream/disp32
+  54     # . . discard args
+  55     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+  56 
+  57     # run tests if necessary, convert stdin if not
+  58     # . prolog
+  59     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  60     # initialize heap
+  61     # - if argc > 1 and argv[1] == "test", then return run_tests()
+  62     # . argc > 1
+  63     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
+  64     7e/jump-if-lesser-or-equal  $run-main/disp8
+  65     # . argv[1] == "test"
+  66     # . . push args
+  67     68/push  "test"/imm32
+  68     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+  69     # . . call
+  70     e8/call  kernel-string-equal?/disp32
+  71     # . . discard args
+  72     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  73     # . check result
+  74     3d/compare-EAX-and  1/imm32
+  75     75/jump-if-not-equal  $run-main/disp8
+  76     # . run-tests()
+  77     e8/call  run-tests/disp32
+  78     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  79     eb/jump  $main:end/disp8
+  80 $run-main:
+  81     # - otherwise convert stdin
+  82     # var ed/EAX : exit-descriptor
+  83     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+  84     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
+  85     # configure ed to really exit()
+  86     # . ed->target = 0
+  87     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+  88     # return convert(Stdin, 1/stdout, 2/stderr, ed)
+  89     # . . push args
+  90     50/push-EAX/ed
+  91     68/push  Stderr/imm32
+  92     68/push  Stdout/imm32
+  93     68/push  Stdin/imm32
+  94     # . . call
+  95     e8/call  convert/disp32
+  96     # . . discard args
+  97     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+  98     # . syscall(exit, 0)
+  99     bb/copy-to-EBX  0/imm32
+ 100 $main:end:
+ 101     b8/copy-to-EAX  1/imm32/exit
+ 102     cd/syscall  0x80/imm8
+ 103 
+ 104 # data structures:
+ 105 #   segment-info: {address, file-offset, size}            (12 bytes)
+ 106 #   segments: (address stream {string, segment-info})     (16 bytes per row)
+ 107 #   label-info: {segment-name, segment-offset, address}   (12 bytes)
+ 108 #   labels: (address stream {string, label-info})         (16 bytes per row)
+ 109 # these are all inefficient; use sequential scans for lookups
+ 110 
+ 111 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+ 112     # pseudocode
+ 113     #   var segments = new-stream(10 rows, 16 bytes each)
+ 114     #   var labels = new-stream(512 rows, 16 bytes each)
+ 115     #   compute-offsets(in, segments, labels)
+ 116     #   compute-addresses(segments, labels)
+ 117     #   rewind-stream(in)
+ 118     #   emit-output(in, out, segments, labels)
+ 119     #
+ 120     # . prolog
+ 121     55/push-EBP
+ 122     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 123     # . save registers
+ 124     51/push-ECX
+ 125     52/push-EDX
+ 126     # var segments/ECX = stream(10 * 16)
+ 127     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
+ 128     68/push  0xa0/imm32/length
+ 129     68/push  0/imm32/read
+ 130     68/push  0/imm32/write
+ 131     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 132     # var labels/EDX = stream(512 * 16)
+ 133     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
+ 134     68/push  0x2000/imm32/length
+ 135     68/push  0/imm32/read
+ 136     68/push  0/imm32/write
+ 137     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 138 +--  9 lines: #?     # write(2/stderr, "compute-offsets\n") --------------------------------------------------------------------------------------------------
+ 147     # compute-offsets(in, segments, labels)
+ 148     # . . push args
+ 149     52/push-EDX
+ 150     51/push-ECX
+ 151     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 152     # . . call
+ 153     e8/call  compute-offsets/disp32
+ 154     # . . discard args
+ 155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 156 +--  9 lines: #?     # write(2/stderr, "compute-addresses\n") ------------------------------------------------------------------------------------------------
+ 165     # compute-addresses(segments, labels)
+ 166     # . . push args
+ 167     52/push-EDX
+ 168     51/push-ECX
+ 169     # . . call
+ 170     e8/call  compute-addresses/disp32
+ 171     # . . discard args
+ 172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
+ 173     # rewind-stream(in)
+ 174     # . . push args
+ 175     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 176     # . . call
+ 177     e8/call  rewind-stream/disp32
+ 178     # . . discard args
+ 179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 180 +--  9 lines: #?     # write(2/stderr, "emit-output\n") ------------------------------------------------------------------------------------------------------
+ 189 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+ 215 +-- 46 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
+ 261     # emit-output(in, out, segments, labels)
+ 262     # . . push args
+ 263     52/push-EDX
+ 264     51/push-ECX
+ 265     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 266     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 267     # . . call
+ 268     e8/call  emit-output/disp32
+ 269     # . . discard args
+ 270     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+ 271 $convert:end:
+ 272     # . reclaim locals
+ 273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
+ 274     # . restore registers
+ 275     5a/pop-to-EDX
+ 276     59/pop-to-ECX
+ 277     # . epilog
+ 278     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 279     5d/pop-to-EBP
+ 280     c3/return
+ 281 
+ 282 test-convert-computes-addresses:
+ 283     # input:
+ 284     #   == code 0x1
+ 285     #   Entry:
+ 286     #   ab x/imm32
+ 287     #   == data 0x1000
+ 288     #   x:
+ 289     #     01
+ 290     #
+ 291     # trace contains (in any order):
+ 292     #   label x is at address 0x1079
+ 293     #   segment code starts at address 0x74
+ 294     #   segment code has size 5
+ 295     #   segment data starts at address 0x1079
+ 296     #
+ 297     # . prolog
+ 298     55/push-EBP
+ 299     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 300     # setup
+ 301     # . clear-stream(_test-input-stream)
+ 302     # . . push args
+ 303     68/push  _test-input-stream/imm32
+ 304     # . . call
+ 305     e8/call  clear-stream/disp32
+ 306     # . . discard args
+ 307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 308     # . clear-stream(_test-input-buffered-file+4)
+ 309     # . . push args
+ 310     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 311     05/add-to-EAX  4/imm32
+ 312     50/push-EAX
+ 313     # . . call
+ 314     e8/call  clear-stream/disp32
+ 315     # . . discard args
+ 316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 317     # . clear-stream(_test-output-stream)
+ 318     # . . push args
+ 319     68/push  _test-output-stream/imm32
+ 320     # . . call
+ 321     e8/call  clear-stream/disp32
+ 322     # . . discard args
+ 323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 324     # . clear-stream(_test-output-buffered-file+4)
+ 325     # . . push args
+ 326     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 327     05/add-to-EAX  4/imm32
+ 328     50/push-EAX
+ 329     # . . call
+ 330     e8/call  clear-stream/disp32
+ 331     # . . discard args
+ 332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 333     # initialize input
+ 334     # . write(_test-input-stream, "== code 0x1\n")
+ 335     # . . push args
+ 336     68/push  "== code 0x1\n"/imm32
+ 337     68/push  _test-input-stream/imm32
+ 338     # . . call
+ 339     e8/call  write/disp32
+ 340     # . . discard args
+ 341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 342     # . write(_test-input-stream, "Entry:\n")
+ 343     # . . push args
+ 344     68/push  "Entry:\n"/imm32
+ 345     68/push  _test-input-stream/imm32
+ 346     # . . call
+ 347     e8/call  write/disp32
+ 348     # . . discard args
+ 349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 350     # . write(_test-input-stream, "ab x/imm32\n")
+ 351     # . . push args
+ 352     68/push  "ab x/imm32\n"/imm32
+ 353     68/push  _test-input-stream/imm32
+ 354     # . . call
+ 355     e8/call  write/disp32
+ 356     # . . discard args
+ 357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 358     # . write(_test-input-stream, "== data 0x1000\n")
+ 359     # . . push args
+ 360     68/push  "== data 0x1000\n"/imm32
+ 361     68/push  _test-input-stream/imm32
+ 362     # . . call
+ 363     e8/call  write/disp32
+ 364     # . . discard args
+ 365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 366     # . write(_test-input-stream, "x:\n")
+ 367     # . . push args
+ 368     68/push  "x:\n"/imm32
+ 369     68/push  _test-input-stream/imm32
+ 370     # . . call
+ 371     e8/call  write/disp32
+ 372     # . . discard args
+ 373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 374     # . write(_test-input-stream, "01\n")
+ 375     # . . push args
+ 376     68/push  "01\n"/imm32
+ 377     68/push  _test-input-stream/imm32
+ 378     # . . call
+ 379     e8/call  write/disp32
+ 380     # . . discard args
+ 381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 382     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 383     # . . push args
+ 384     68/push  _test-output-buffered-file/imm32
+ 385     68/push  _test-input-buffered-file/imm32
+ 386     # . . call
+ 387     e8/call  convert/disp32
+ 388     # . . discard args
+ 389     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 390     # check trace
+ 391 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+ 417     # . check-trace-contains("label 'x' is at address 0x00001079.", msg)
+ 418     # . . push args
+ 419     68/push  "F - test-convert-computes-addresses/0"/imm32
+ 420     68/push  "label 'x' is at address 0x00001079."/imm32
+ 421     # . . call
+ 422     e8/call  check-trace-contains/disp32
+ 423     # . . discard args
+ 424     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 425     # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
+ 426     # . . push args
+ 427     68/push  "F - test-convert-computes-addresses/1"/imm32
+ 428     68/push  "segment 'code' starts at address 0x00000074."/imm32
+ 429     # . . call
+ 430     e8/call  check-trace-contains/disp32
+ 431     # . . discard args
+ 432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 433     # . check-trace-contains("segment 'code' has size 0x00000005.", msg)
+ 434     # . . push args
+ 435     68/push  "F - test-convert-computes-addresses/2"/imm32
+ 436     68/push  "segment 'code' has size 0x00000005."/imm32
+ 437     # . . call
+ 438     e8/call  check-trace-contains/disp32
+ 439     # . . discard args
+ 440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 441     # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
+ 442     # . . push args
+ 443     68/push  "F - test-convert-computes-addresses/3"/imm32
+ 444     68/push  "segment 'data' starts at address 0x00001079."/imm32
+ 445     # . . call
+ 446     e8/call  check-trace-contains/disp32
+ 447     # . . discard args
+ 448     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 449     # . epilog
+ 450     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 451     5d/pop-to-EBP
+ 452     c3/return
+ 453 
+ 454 # global scratch space for compute-offsets in the data segment
+ 455 == data
+ 456 
+ 457 compute-offsets:file-offset:  # int
+ 458   0/imm32
+ 459 compute-offsets:segment-offset:  # int
+ 460   0/imm32
+ 461 compute-offsets:word-slice:
+ 462   0/imm32/start
+ 463   0/imm32/end
+ 464 compute-offsets:segment-tmp:  # slice
+ 465   0/imm32/start
+ 466   0/imm32/end
+ 467 
+ 468 == code
+ 469 
+ 470 compute-offsets:  # in : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+ 471     # skeleton:
+ 472     #   for lines in 'in'
+ 473     #     for words in line
+ 474     #       switch word
+ 475     #         case 1
+ 476     #         case 2
+ 477     #         ...
+ 478     #         default
+ 479     #
+ 480     # pseudocode:
+ 481     #   curr-segment-name : (address string) = 0
+ 482     #   var line = new-stream(512, 1)
+ 483     #   while true                                  # line loop
+ 484     #     clear-stream(line)
+ 485     #     read-line-buffered(in, line)
+ 486     #     if (line->write == 0) break               # end of file
+ 487     #     while true                                # word loop
+ 488     #       word-slice = next-word(line)
+ 489     #       if slice-empty?(word-slice)             # end of line
+ 490     #         break
+ 491     #       else if slice-starts-with?(word-slice, "#")  # comment
+ 492     #         continue
+ 493     #       else if slice-equal?(word-slice, "==")
+ 494     #         if curr-segment-name != 0
+ 495     #           seg = get-or-insert(segments, curr-segment-name)
+ 496     #           seg->size = *file-offset - seg->file-offset
+ 497     #           trace("segment '", curr-segment-name, "' has size ", seg->size)
+ 498     #         segment-tmp = next-word(line)
+ 499     #         curr-segment-name = slice-to-string(segment-tmp)
+ 500     #         if empty?(curr-segment-name)
+ 501     #           abort
+ 502     #         segment-tmp = next-word(line)
+ 503     #         if slice-empty?(segment-tmp)
+ 504     #           abort
+ 505     #         seg = get-or-insert(segments, curr-segment-name)
+ 506     #         seg->starting-address = parse-hex-int(segment-tmp)
+ 507     #         seg->file-offset = *file-offset
+ 508     #         trace("segment '", curr-segment-name, "' is at file offset ", seg->file-offset)
+ 509     #         segment-offset = 0
+ 510     #         break  (next line)
+ 511     #       else if is-label?(word-slice)
+ 512     #         strip trailing ':' from word-slice
+ 513     #         x : (address label-info) = get-or-insert(labels, name)
+ 514     #         x->segment-name = curr-segment-name
+ 515     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
+ 516     #         x->segment-offset = segment-offset
+ 517     #         trace("label '", word-slice, "' is at segment offset ", segment-offset, ".")
+ 518     #         # labels occupy no space, so no need to increment offsets
+ 519     #       else
+ 520     #         width = compute-width-of-slice(word-slice)
+ 521     #         *segment-offset += width
+ 522     #         *file-offset += width
+ 523     #
+ 524     # . prolog
+ 525     55/push-EBP
+ 526     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 527     # . save registers
+ 528     50/push-EAX
+ 529     51/push-ECX
+ 530     52/push-EDX
+ 531     53/push-EBX
+ 532     56/push-ESI
+ 533     57/push-EDI
+ 534     # curr-segment-name/ESI = 0
+ 535     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
+ 536     # file-offset = 0
+ 537     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:file-offset/disp32  0/imm32               # copy to *compute-offsets:word-slice
+ 538     # segment-offset = 0
+ 539     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:segment-offset/disp32  0/imm32            # copy to *compute-offsets:word-slice
+ 540     # line/ECX = new-stream(512, 1)
+ 541     # . EAX = new-stream(512, 1)
+ 542     # . . push args
+ 543     68/push  1/imm32
+ 544     68/push  0x200/imm32
+ 545     68/push  Heap/imm32
+ 546     # . . call
+ 547     e8/call  new-stream/disp32
+ 548     # . . discard args
+ 549     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 550     # . line/ECX = EAX
+ 551     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+ 552 $compute-offsets:line-loop:
+ 553     # clear-stream(line/ECX)
+ 554     51/push-ECX
+ 555     e8/call  clear-stream/disp32
+ 556     # . discard args
+ 557     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 558     # read-line-buffered(in, line/ECX)
+ 559     51/push-ECX
+ 560     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 561     e8/call  read-line-buffered/disp32
+ 562     # . discard args
+ 563     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 564     # if (line->write == 0) break
+ 565     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+ 566     3d/compare-EAX-and  0/imm32
+ 567     0f 84/jump-if-equal  $compute-offsets:break-line-loop/disp32
+ 568 $compute-offsets:word-loop:
+ 569     # EDX = word-slice
+ 570     ba/copy-to-EDX  compute-offsets:word-slice/imm32
+ 571     # next-word(line/ECX, word-slice/EDX)
+ 572     52/push-EDX
+ 573     51/push-ECX
+ 574     e8/call  next-word/disp32
+ 575     # . discard args
+ 576     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 577 +-- 80 lines: #?     # dump word-slice and maybe curr-segment-name -------------------------------------------------------------------------------------------
+ 657 $compute-offsets:check0:
+ 658     # if slice-empty?(word/EDX) break
+ 659     # . EAX = slice-empty?(word/EDX)
+ 660     52/push-EDX
+ 661     e8/call  slice-empty?/disp32
+ 662     # . . discard args
+ 663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 664     # . if (EAX != 0) break
+ 665     3d/compare-EAX-and  0/imm32
+ 666     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
+ 667     # if slice-starts-with?(word-slice, "#") continue
+ 668     68/push  "#"/imm32
+ 669     52/push-EDX
+ 670     e8/call  slice-starts-with?/disp32
+ 671     # . . discard args
+ 672     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 673     # . if (EAX != 0) continue
+ 674     3d/compare-EAX-and  0/imm32
+ 675     0f 85/jump-if-not-equal  $compute-offsets:word-loop/disp32
+ 676 $compute-offsets:case-segment-header:
+ 677     # if (!slice-equal?(word-slice/EDX, "==")) goto next case
+ 678     # . EAX = slice-equal?(word-slice/EDX, "==")
+ 679     68/push  "=="/imm32
+ 680     52/push-EDX
+ 681     e8/call  slice-equal?/disp32
+ 682     # . . discard args
+ 683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 684     # . if (EAX == 0) goto next case
+ 685     3d/compare-EAX-and  0/imm32
+ 686     0f 84/jump-if-equal  $compute-offsets:case-label/disp32
+ 687     # if (curr-segment-name == 0) goto construct-next-segment
+ 688     81          7/subop/compare     3/mod/direct    6/rm32/ESI    .           .             .           .           .               0/imm32           # compare ESI
+ 689     74/jump-if-equal  $compute-offsets:construct-next-segment/disp8
+ 690     # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16)
+ 691     # . . push args
+ 692     68/push  0x10/imm32/row-size
+ 693     56/push-ESI
+ 694     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 695     # . . call
+ 696     e8/call  get-or-insert/disp32
+ 697     # . . discard args
+ 698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 699     # seg->size = file-offset - seg->file-offset
+ 700     # . save ECX
+ 701     51/push-ECX
+ 702     # . EBX = *file-offset
+ 703     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy *file-offset to EBX
+ 704     # . ECX = seg->file-offset
+ 705     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EAX+4) to ECX
+ 706     # . EBX -= ECX
+ 707     29/subtract                     3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EBX
+ 708     # . seg->size = EBX
+ 709     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy EBX to *(EAX+8)
+ 710     # . restore ECX
+ 711     59/pop-to-ECX
+ 712     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
+ 713     # . . push args
+ 714     68/push  "."/imm32
+ 715     53/push-EBX
+ 716     68/push  "' has size "/imm32
+ 717     56/push-ESI
+ 718     68/push  "segment '"/imm32
+ 719     # . . call
+ 720     e8/call  trace-sssns/disp32
+ 721     # . . discard args
+ 722     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+ 723 $compute-offsets:construct-next-segment:
+ 724     # next-word(line/ECX, segment-tmp)
+ 725     68/push  compute-offsets:segment-tmp/imm32
+ 726     51/push-ECX
+ 727     e8/call  next-word/disp32
+ 728     # . discard args
+ 729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 730 +-- 47 lines: #?     # dump curr-segment-name if not null (clobbering EAX) -----------------------------------------------------------------------------------
+ 777 $compute-offsets:update-curr-segment-name:
+ 778     # curr-segment-name = slice-to-string(segment-tmp)
+ 779     # . EAX = slice-to-string(Heap, segment-tmp)
+ 780     # . . push args
+ 781     68/push  compute-offsets:segment-tmp/imm32
+ 782     68/push  Heap/imm32
+ 783     # . . call
+ 784     e8/call  slice-to-string/disp32
+ 785     # . . discard args
+ 786     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 787     # . curr-segment-name = EAX
+ 788     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
+ 789     # if empty?(curr-segment-name) abort
+ 790     # . if (EAX == 0) abort
+ 791     3d/compare-EAX-and  0/imm32
+ 792     0f 84/jump-if-equal  $compute-offsets:abort/disp32
+ 793     # next-word(line/ECX, segment-tmp)
+ 794     68/push  compute-offsets:segment-tmp/imm32
+ 795     51/push-ECX
+ 796     e8/call  next-word/disp32
+ 797     # . discard args
+ 798     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 799     # if slice-empty?(segment-tmp) abort
+ 800     # . EAX = slice-empty?(segment-tmp)
+ 801     68/push  compute-offsets:segment-tmp/imm32
+ 802     e8/call  slice-empty?/disp32
+ 803     # . . discard args
+ 804     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 805     # . if (EAX != 0) abort
+ 806     3d/compare-EAX-and  0/imm32
+ 807     0f 85/jump-if-not-equal  $compute-offsets:abort/disp32
+ 808     # seg/EBX = get-or-insert(segments, curr-segment-name, row-size=16)
+ 809     # . . push args
+ 810     68/push  0x10/imm32/row-size
+ 811     56/push-ESI
+ 812     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 813     # . . call
+ 814     e8/call  get-or-insert/disp32
+ 815     # . . discard args
+ 816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 817     # . EBX = EAX
+ 818     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+ 819     # seg->address = parse-hex-int(segment-tmp)
+ 820     # . EAX = parse-hex-int(segment-tmp)
+ 821     68/push  compute-offsets:segment-tmp/imm32
+ 822     e8/call  parse-hex-int/disp32
+ 823     # . . discard args
+ 824     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 825     # . seg->address = EAX
+ 826     89/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EBX
+ 827     # seg->file-offset = *file-offset
+ 828     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:file-offset/disp32 # copy *file-offset to EAX
+ 829     89/copy                         1/mod/*+disp8   3/rm32/EBX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EBX+4)
+ 830     # trace-sssns("segment '", curr-segment-name, "' is at file offset ", seg->file-offset, "")
+ 831     # . . push args
+ 832     68/push  "."/imm32
+ 833     50/push-EAX
+ 834     68/push  "' is at file offset "/imm32
+ 835     56/push-ESI
+ 836     68/push  "segment '"/imm32
+ 837     # . . call
+ 838     e8/call  trace-sssns/disp32
+ 839     # . . discard args
+ 840     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+ 841     # segment-offset = 0
+ 842     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     compute-offsets:segment-offset/disp32  0/imm32           # copy to *segment-offset
+ 843     # break
+ 844     e9/jump $compute-offsets:line-loop/disp32
+ 845 $compute-offsets:case-label:
+ 846     # if (!is-label?(word-slice/EDX)) goto next case
+ 847     # . EAX = is-label?(word-slice/EDX)
+ 848     # . . push args
+ 849     52/push-EDX
+ 850     # . . call
+ 851     e8/call  is-label?/disp32
+ 852     # . . discard args
+ 853     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 854     # . if (EAX == 0) goto next case
+ 855     3d/compare-EAX-and  0/imm32
+ 856     74/jump-if-equal  $compute-offsets:case-default/disp8
+ 857     # strip trailing ':' from word-slice
+ 858     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # decrement *(EDX+4)
+ 859     # x/EAX = leaky-get-or-insert-slice(labels, word-slice, row-size=16)
+ 860     # . . push args
+ 861     68/push  0x10/imm32/row-size
+ 862     52/push-EDX
+ 863     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+ 864     # . . call
+ 865     e8/call  leaky-get-or-insert-slice/disp32
+ 866     # . . discard args
+ 867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 868 $compute-offsets:save-label-offset:
+ 869     # x->segment-name = curr-segment-name
+ 870     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to *EAX
+ 871     # trace-slsss("label '" word-slice/EDX "' is in segment '" current-segment-name "'.")
+ 872     # . . push args
+ 873     68/push  "'."/imm32
+ 874     56/push-ESI
+ 875     68/push  "' is in segment '"/imm32
+ 876     52/push-EDX
+ 877     68/push  "label '"/imm32
+ 878     # . . call
+ 879     e8/call  trace-slsss/disp32
+ 880     # . . discard args
+ 881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+ 882     # x->segment-offset = segment-offset
+ 883     # . EBX = segment-offset
+ 884     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:segment-offset/disp32  # copy *segment-offset to EBX
+ 885     # . x->segment-offset = EBX
+ 886     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EAX+4)
+ 887     # trace-slsns("label '" word-slice/EDX "' is at segment offset " *segment-offset/EAX ".")
+ 888     # . . EAX = file-offset
+ 889     b8/copy-to-EAX compute-offsets:segment-offset/imm32
+ 890     # . . EAX = *file-offset/EAX
+ 891     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
+ 892     # . . push args
+ 893     68/push  "."/imm32
+ 894     50/push-EAX
+ 895     68/push  "' is at segment offset "/imm32
+ 896     52/push-EDX
+ 897     68/push  "label '"/imm32
+ 898     # . . call
+ 899     e8/call  trace-slsns/disp32
+ 900     # . . discard args
+ 901     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+ 902     # continue
+ 903     e9/jump  $compute-offsets:word-loop/disp32
+ 904 $compute-offsets:case-default:
+ 905     # width/EAX = compute-width-of-slice(word-slice)
+ 906     # . . push args
+ 907     52/push-EDX
+ 908     # . . call
+ 909     e8/call compute-width-of-slice/disp32
+ 910     # . . discard args
+ 911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 912     # segment-offset += width
+ 913     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:segment-offset/disp32 # add EAX to *segment-offset
+ 914     # file-offset += width
+ 915     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:file-offset/disp32 # add EAX to *file-offset
+ 916 +-- 47 lines: #?     # dump segment-offset -------------------------------------------------------------------------------------------------------------------
+ 963     e9/jump $compute-offsets:word-loop/disp32
+ 964 $compute-offsets:break-line-loop:
+ 965     # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16)
+ 966     # . . push args
+ 967     68/push  0x10/imm32/row-size
+ 968     56/push-ESI
+ 969     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 970     # . . call
+ 971     e8/call  get-or-insert/disp32
+ 972     # . . discard args
+ 973     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 974     # seg->size = file-offset - seg->file-offset
+ 975     # . save ECX
+ 976     51/push-ECX
+ 977     # . EBX = *file-offset
+ 978     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy *file-offset to EBX
+ 979     # . ECX = seg->file-offset
+ 980     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EAX+4) to ECX
+ 981     # . EBX -= ECX
+ 982     29/subtract                     3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EBX
+ 983     # . seg->size = EBX
+ 984     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy EBX to *(EAX+8)
+ 985     # . restore ECX
+ 986     59/pop-to-ECX
+ 987     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
+ 988     # . trace-sssns("segment '", curr-segment-name, "' has size ", EBX, ".")
+ 989     # . . push args
+ 990     68/push  "."/imm32
+ 991     53/push-EBX
+ 992     68/push  "' has size "/imm32
+ 993     56/push-ESI
+ 994     68/push  "segment '"/imm32
+ 995     # . . call
+ 996     e8/call  trace-sssns/disp32
+ 997     # . . discard args
+ 998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+ 999 $compute-offsets:end:
+1000     # . reclaim locals
+1001     # . restore registers
+1002     5f/pop-to-EDI
+1003     5e/pop-to-ESI
+1004     5b/pop-to-EBX
+1005     5a/pop-to-EDX
+1006     59/pop-to-ECX
+1007     58/pop-to-EAX
+1008     # . epilog
+1009     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1010     5d/pop-to-EBP
+1011     c3/return
+1012 $compute-offsets:abort:
+1013     # . _write(2/stderr, error)
+1014     # . . push args
+1015     68/push  "'==' must be followed by segment name and segment-start\n"/imm32
+1016     68/push  2/imm32/stderr
+1017     # . . call
+1018     e8/call  _write/disp32
+1019     # . . discard args
+1020     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1021     # . syscall(exit, 1)
+1022     bb/copy-to-EBX  1/imm32
+1023     b8/copy-to-EAX  1/imm32/exit
+1024     cd/syscall  0x80/imm8
+1025     # never gets here
+1026 
+1027 test-compute-offsets:
+1028     # input:
+1029     #   == code 0x1
+1030     #   ab x/imm32
+1031     #   == data 0x1000
+1032     #   00
+1033     #   x:
+1034     #     34
+1035     #
+1036     # trace contains (in any order):
+1037     #   segment 'code' is at file offset 0x0.
+1038     #   segment 'code' has size 0x5.
+1039     #   segment 'data' is at file offset 0x5.
+1040     #   segment 'data' has size 0x2.
+1041     #   label 'x' is in segment 'data'.
+1042     #   label 'x' is at segment offset 0x1.
+1043     #
+1044     # . prolog
+1045     55/push-EBP
+1046     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1047     # setup
+1048     # . clear-stream(_test-input-stream)
+1049     # . . push args
+1050     68/push  _test-input-stream/imm32
+1051     # . . call
+1052     e8/call  clear-stream/disp32
+1053     # . . discard args
+1054     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1055     # . clear-stream(_test-input-buffered-file+4)
+1056     # . . push args
+1057     b8/copy-to-EAX  _test-input-buffered-file/imm32
+1058     05/add-to-EAX  4/imm32
+1059     50/push-EAX
+1060     # . . call
+1061     e8/call  clear-stream/disp32
+1062     # . . discard args
+1063     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1064     # . clear-stream(_test-output-stream)
+1065     # . . push args
+1066     68/push  _test-output-stream/imm32
+1067     # . . call
+1068     e8/call  clear-stream/disp32
+1069     # . . discard args
+1070     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1071     # . clear-stream(_test-output-buffered-file+4)
+1072     # . . push args
+1073     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1074     05/add-to-EAX  4/imm32
+1075     50/push-EAX
+1076     # . . call
+1077     e8/call  clear-stream/disp32
+1078     # . . discard args
+1079     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1080     # var segments/ECX = stream(2 * 16)
+1081     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x20/imm32        # subtract from ESP
+1082     68/push  0x20/imm32/length
+1083     68/push  0/imm32/read
+1084     68/push  0/imm32/write
+1085     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1086     # var labels/EDX = stream(2 * 16)
+1087     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x20/imm32        # subtract from ESP
+1088     68/push  0x20/imm32/length
+1089     68/push  0/imm32/read
+1090     68/push  0/imm32/write
+1091     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+1092     # initialize input
+1093     # . write(_test-input-stream, "== code 0x1\n")
+1094     # . . push args
+1095     68/push  "== code 0x1\n"/imm32
+1096     68/push  _test-input-stream/imm32
+1097     # . . call
+1098     e8/call  write/disp32
+1099     # . . discard args
+1100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1101     # . write(_test-input-stream, "ab x/imm32\n")
+1102     # . . push args
+1103     68/push  "ab x/imm32\n"/imm32
+1104     68/push  _test-input-stream/imm32
+1105     # . . call
+1106     e8/call  write/disp32
+1107     # . . discard args
+1108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1109     # . write(_test-input-stream, "== data 0x1000\n")
+1110     # . . push args
+1111     68/push  "== data 0x1000\n"/imm32
+1112     68/push  _test-input-stream/imm32
+1113     # . . call
+1114     e8/call  write/disp32
+1115     # . . discard args
+1116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1117     # . write(_test-input-stream, "00\n")
+1118     # . . push args
+1119     68/push  "00\n"/imm32
+1120     68/push  _test-input-stream/imm32
+1121     # . . call
+1122     e8/call  write/disp32
+1123     # . . discard args
+1124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1125     # . write(_test-input-stream, "x:\n")
+1126     # . . push args
+1127     68/push  "x:\n"/imm32
+1128     68/push  _test-input-stream/imm32
+1129     # . . call
+1130     e8/call  write/disp32
+1131     # . . discard args
+1132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1133     # . write(_test-input-stream, "34\n")
+1134     # . . push args
+1135     68/push  "34\n"/imm32
+1136     68/push  _test-input-stream/imm32
+1137     # . . call
+1138     e8/call  write/disp32
+1139     # . . discard args
+1140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1141     # compute-offsets(_test-input-buffered-file, segments, labels)
+1142     # . . push args
+1143     52/push-EDX
+1144     51/push-ECX
+1145     68/push  _test-input-buffered-file/imm32
+1146     # . . call
+1147     e8/call  compute-offsets/disp32
+1148     # . . discard args
+1149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32        # add to ESP
+1150 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+1176     # check trace
+1177     # . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg)
+1178     # . . push args
+1179     68/push  "F - test-compute-offsets/0"/imm32
+1180     68/push  "segment 'code' is at file offset 0x00000000."/imm32
+1181     # . . call
+1182     e8/call  check-trace-contains/disp32
+1183     # . . discard args
+1184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1185     # . check-trace-contains("segment 'code' has size 0x00000005", msg)
+1186     # . . push args
+1187     68/push  "F - test-compute-offsets/1"/imm32
+1188     68/push  "segment 'code' has size 0x00000005."/imm32
+1189     # . . call
+1190     e8/call  check-trace-contains/disp32
+1191     # . . discard args
+1192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1193     # . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg)
+1194     # . . push args
+1195     68/push  "F - test-compute-offsets/2"/imm32
+1196     68/push  "segment 'data' is at file offset 0x00000005."/imm32
+1197     # . . call
+1198     e8/call  check-trace-contains/disp32
+1199     # . . discard args
+1200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1201     # . check-trace-contains("segment 'data' has size 0x00000002.", msg)
+1202     # . . push args
+1203     68/push  "F - test-compute-offsets/3"/imm32
+1204     68/push  "segment 'data' has size 0x00000002."/imm32
+1205     # . . call
+1206     e8/call  check-trace-contains/disp32
+1207     # . . discard args
+1208     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1209     # . check-trace-contains("label 'x' is in segment 'data'.", msg)
+1210     # . . push args
+1211     68/push  "F - test-compute-offsets/4"/imm32
+1212     68/push  "label 'x' is in segment 'data'."/imm32
+1213     # . . call
+1214     e8/call  check-trace-contains/disp32
+1215     # . . discard args
+1216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1217     # . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg)
+1218     # . . push args
+1219     68/push  "F - test-compute-offsets/5"/imm32
+1220     68/push  "label 'x' is at segment offset 0x00000001."/imm32
+1221     # . . call
+1222     e8/call  check-trace-contains/disp32
+1223     # . . discard args
+1224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1225     # . check-ints-equal(labels->write, 0x10, msg)
+1226     # . . push args
+1227     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
+1228     68/push  0x10/imm32/1-entry
+1229     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
+1230     # . . call
+1231     e8/call  check-ints-equal/disp32
+1232     # . . discard args
+1233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1234     # . epilog
+1235     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1236     5d/pop-to-EBP
+1237     c3/return
+1238 
+1239 compute-addresses:  # segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+1240     # pseudocode:
+1241     #   srow : (address segment-info) = segments->data
+1242     #   max = segments->data + segments->write
+1243     #   num-segments = segments->write / 16
+1244     #   starting-offset = 0x34 + (num-segments * 0x20)
+1245     #   while true
+1246     #     if (srow >= max) break
+1247     #     s->file-offset += starting-offset
+1248     #     s->address &= 0xfffff000  # clear last 12 bits for p_align
+1249     #     s->address += (s->file-offset & 0x00000fff)
+1250     #     trace-sssns("segment " s->key " starts at address " s->address)
+1251     #     srow += 16  # row-size
+1252     #   lrow : (address label-info) = labels->data
+1253     #   max = labels->data + labels->write
+1254     #   while true
+1255     #     if (lrow >= max) break
+1256     #     seg-name : (address string) = lrow->segment-name
+1257     #     label-seg : (address segment-info) = get(segments, seg-name, row-size=16)
+1258     #     lrow->address = label-seg->address + lrow->segment-offset
+1259     #     trace-sssns("label " lrow->key " is at address " lrow->address)
+1260     #     lrow += 16  # row-size
+1261     #
+1262     # . prolog
+1263     55/push-EBP
+1264     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1265     # . save registers
+1266     50/push-EAX
+1267     51/push-ECX
+1268     52/push-EDX
+1269     53/push-EBX
+1270     56/push-ESI
+1271     57/push-EDI
+1272     # ESI = segments
+1273     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+1274     # starting-offset/EDI = 0x34 + (num-segments * 0x20)  # make room for ELF headers
+1275     # . EDI = segments->write / 16 (row-size)
+1276     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           7/r32/EDI   .               .                 # copy *ESI to EDI
+1277     c1/shift    5/subop/logic-right 3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm8            # shift EDI right by 4 bits, while padding zeroes
+1278     # . EDI = (EDI * 0x20) + 0x34
+1279     c1/shift    4/subop/left        3/mod/direct    7/rm32/EDI    .           .             .           .           .               5/imm8            # shift EDI left by 5 bits
+1280     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               0x34/imm32        # add to EDI
+1281     # srow/EAX = segments->data
+1282     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
+1283     # max/ECX = segments->data + segments->write
+1284     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+1285     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # add ESI to ECX
+1286 $compute-addresses:segment-loop:
+1287     # if (srow >= max) break
+1288     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+1289     73/jump-if-greater-or-equal-unsigned  $compute-addresses:segment-break/disp8
+1290     # srow->file-offset += starting-offset
+1291     01/add                          1/mod/*+disp8   0/rm32/EAX    .           .             .           7/r32/EDI   8/disp8         .                 # add EDI to *(EAX+8)
+1292     # clear last 12 bits of srow->address for p_align=0x1000
+1293     # . EDX = srow->address
+1294     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(EAX+4) to EDX
+1295     # . EDX &= 0xfffff000
+1296     81          4/subop/and         3/mod/direct    2/rm32/EDX    .           .             .           .           .               0xfffff000/imm32  # bitwise and of EDX
+1297     # update last 12 bits from srow->file-offset
+1298     # . EBX = srow->file-offset
+1299     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EAX+8) to EBX
+1300     # . EBX &= 0xfff
+1301     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x00000fff/imm32  # bitwise and of EBX
+1302     # . srow->address = EDX | EBX
+1303     09/or                           3/mod/direct    2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # EDX = bitwise OR with EBX
+1304     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy EDX to *(EAX+4)
+1305     # trace-sssns("segment " srow " starts at address " srow->address ".")
+1306     # . . push args
+1307     68/push  "."/imm32
+1308     52/push-EDX
+1309     68/push  "' starts at address "/imm32
+1310     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
+1311     68/push  "segment '"/imm32
+1312     # . . call
+1313     e8/call  trace-sssns/disp32
+1314     # . . discard args
+1315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+1316     # srow += 16  # size of row
+1317     05/add-to-EAX  0x10/imm32
+1318     eb/jump  $compute-addresses:segment-loop/disp8
+1319 $compute-addresses:segment-break:
+1320 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+1346     # ESI = labels
+1347     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+1348     # lrow/EAX = labels->data
+1349     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
+1350     # max/ECX = labels->data + labels->write
+1351     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+1352     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # add ESI to ECX
+1353 $compute-addresses:label-loop:
+1354     # if (lrow >= max) break
+1355     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+1356     0f 83/jump-if-greater-or-equal-unsigned  $compute-addresses:end/disp32
+1357 +-- 26 lines: #?     # dump lrow->key ------------------------------------------------------------------------------------------------------------------------
+1383     # seg-name/EDX = lrow->segment-name
+1384     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *EAX to EDX
+1385 +-- 26 lines: #?     # dump seg-name -------------------------------------------------------------------------------------------------------------------------
+1411     # label-seg/EDX : (address segment-info) = get(segments, seg-name, row-size=16)
+1412     # . save EAX
+1413     50/push-EAX
+1414     # . EAX = get(segments, seg-name, row-size=16)
+1415     # . . push args
+1416     68/push  0x10/imm32/row-size
+1417     52/push-EDX
+1418     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1419     # . . call
+1420     e8/call  get/disp32
+1421     # . . discard args
+1422     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1423     # . EDX = EAX
+1424     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
+1425     # . restore EAX
+1426     58/pop-to-EAX
+1427     # EBX = label-seg->address
+1428     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
+1429     # EBX += lrow->segment-offset
+1430     03/add                          1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # add *(EAX+8) to EBX
+1431     # lrow->address = EBX
+1432     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy EBX to *(EAX+12)
+1433     # trace-sssns("label " lrow->key " is at address " lrow->address ".")
+1434     # . . push args
+1435     68/push  "."/imm32
+1436     53/push-EBX
+1437     68/push  "' is at address "/imm32
+1438     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
+1439     68/push  "label '"/imm32
+1440     # . . call
+1441     e8/call  trace-sssns/disp32
+1442     # . . discard args
+1443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+1444     # lrow += 16  # size of row
+1445     05/add-to-EAX  0x10/imm32
+1446     e9/jump  $compute-addresses:label-loop/disp32
+1447 $compute-addresses:end:
+1448     # . restore registers
+1449     5f/pop-to-EDI
+1450     5e/pop-to-ESI
+1451     5b/pop-to-EBX
+1452     5a/pop-to-EDX
+1453     59/pop-to-ECX
+1454     58/pop-to-EAX
+1455     # . epilog
+1456     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1457     5d/pop-to-EBP
+1458     c3/return
+1459 
+1460 test-compute-addresses:
+1461     # input:
+1462     #   segments:
+1463     #     - 'a': {0x1000, 0, 5}
+1464     #     - 'b': {0x2018, 5, 1}
+1465     #     - 'c': {0x5444, 6, 12}
+1466     #   labels:
+1467     #     - 'l1': {'a', 3, 0}
+1468     #     - 'l2': {'b', 0, 0}
+1469     #
+1470     # trace contains in any order (comments in parens):
+1471     #   segment 'a' starts at address 0x00001094.  (0x34 + 0x20 for each segment)
+1472     #   segment 'b' starts at address 0x00002099.  (0x018 discarded)
+1473     #   segment 'c' starts at address 0x0000509a.  (0x444 discarded)
+1474     #   label 'l1' is at address 0x00001097.       (0x1094 + segment-offset 3)
+1475     #   label 'l2' is at address 0x00002099.       (0x2099 + segment-offset 0)
+1476     #
+1477     # . prolog
+1478     55/push-EBP
+1479     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1480     # setup
+1481     # . var segments/ECX = stream(10 * 16)
+1482     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
+1483     68/push  0xa0/imm32/length
+1484     68/push  0/imm32/read
+1485     68/push  0/imm32/write
+1486     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1487     # . var labels/EDX = stream(512 * 16)
+1488     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
+1489     68/push  0x2000/imm32/length
+1490     68/push  0/imm32/read
+1491     68/push  0/imm32/write
+1492     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+1493     # . stream-add4(segments, "a", 0x1000, 0, 5)
+1494     68/push  5/imm32/segment-size
+1495     68/push  0/imm32/file-offset
+1496     68/push  0x1000/imm32/start-address
+1497     68/push  "a"/imm32/segment-name
+1498     51/push-ECX
+1499     # . . call
+1500     e8/call  stream-add4/disp32
+1501     # . . discard args
+1502     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+1503     # . stream-add4(segments, "b", 0x2018, 5, 1)
+1504     68/push  1/imm32/segment-size
+1505     68/push  5/imm32/file-offset
+1506     68/push  0x2018/imm32/start-address
+1507     68/push  "b"/imm32/segment-name
+1508     51/push-ECX
+1509     # . . call
+1510     e8/call  stream-add4/disp32
+1511     # . . discard args
+1512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+1513     # . stream-add4(segments, "c", 0x5444, 6, 12)
+1514     68/push  0xc/imm32/segment-size
+1515     68/push  6/imm32/file-offset
+1516     68/push  0x5444/imm32/start-address
+1517     68/push  "c"/imm32/segment-name
+1518     51/push-ECX
+1519     # . . call
+1520     e8/call  stream-add4/disp32
+1521     # . . discard args
+1522     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+1523     # . stream-add4(labels, "l1", "a", 3, 0)
+1524     68/push  0/imm32/label-address
+1525     68/push  3/imm32/segment-offset
+1526     68/push  "a"/imm32/segment-name
+1527     68/push  "l1"/imm32/label-name
+1528     52/push-EDX
+1529     # . . call
+1530     e8/call  stream-add4/disp32
+1531     # . . discard args
+1532     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+1533     # . stream-add4(labels, "l2", "b", 0, 0)
+1534     68/push  0/imm32/label-address
+1535     68/push  0/imm32/segment-offset
+1536     68/push  "b"/imm32/segment-name
+1537     68/push  "l2"/imm32/label-name
+1538     52/push-EDX
+1539     # . . call
+1540     e8/call  stream-add4/disp32
+1541     # . . discard args
+1542     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+1543     # component under test
+1544     # . compute-addresses(segments, labels)
+1545     # . . push args
+1546     52/push-EDX
+1547     51/push-ECX
+1548     # . . call
+1549     e8/call  compute-addresses/disp32
+1550     # . . discard args
+1551     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1552     # checks
+1553 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+1579     # . check-trace-contains("segment 'a' starts at address 0x00001094.", msg)
+1580     # . . push args
+1581     68/push  "F - test-compute-addresses/0"/imm32
+1582     68/push  "segment 'a' starts at address 0x00001094."/imm32
+1583     # . . call
+1584     e8/call  check-trace-contains/disp32
+1585     # . . discard args
+1586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1587     # . check-trace-contains("segment 'b' starts at address 0x00002099.", msg)
+1588     # . . push args
+1589     68/push  "F - test-compute-addresses/1"/imm32
+1590     68/push  "segment 'b' starts at address 0x00002099."/imm32
+1591     # . . call
+1592     e8/call  check-trace-contains/disp32
+1593     # . . discard args
+1594     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1595     # . check-trace-contains("segment 'c' starts at address 0x0000509a.", msg)
+1596     # . . push args
+1597     68/push  "F - test-compute-addresses/2"/imm32
+1598     68/push  "segment 'c' starts at address 0x0000509a."/imm32
+1599     # . . call
+1600     e8/call  check-trace-contains/disp32
+1601     # . . discard args
+1602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1603     # . check-trace-contains("label 'l1' is at address 0x00001097.", msg)
+1604     # . . push args
+1605     68/push  "F - test-compute-addresses/3"/imm32
+1606     68/push  "label 'l1' is at address 0x00001097."/imm32
+1607     # . . call
+1608     e8/call  check-trace-contains/disp32
+1609     # . . discard args
+1610     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1611     # . check-trace-contains("label 'l2' is at address 0x00002099.", msg)
+1612     # . . push args
+1613     68/push  "F - test-compute-addresses/4"/imm32
+1614     68/push  "label 'l2' is at address 0x00002099."/imm32
+1615     # . . call
+1616     e8/call  check-trace-contains/disp32
+1617     # . . discard args
+1618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1619     # . check-ints-equal(labels->write, 0x20, msg)
+1620     # . . push args
+1621     68/push  "F - test-compute-addresses-maintains-labels-write-index"/imm32
+1622     68/push  0x20/imm32/2-entries
+1623     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
+1624     # . . call
+1625     e8/call  check-ints-equal/disp32
+1626     # . . discard args
+1627     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1628     # . epilog
+1629     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1630     5d/pop-to-EBP
+1631     c3/return
+1632 
+1633 emit-output:  # in : (address buffered-file), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+1634     # pseudocode:
+1635     #   emit-headers(out, segments, labels)
+1636     #   emit-segments(in, out, segments, labels)
+1637     #
+1638     # . prolog
+1639     55/push-EBP
+1640     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1641 +--  9 lines: #?     # write(2/stderr, "emit-headers\n") -----------------------------------------------------------------------------------------------------
+1650     # emit-headers(out, segments, labels)
+1651     # . . push args
+1652     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8       .                # push *(EBP+20)
+1653     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8       .                # push *(EBP+16)
+1654     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8        .                # push *(EBP+12)
+1655     # . . call
+1656     e8/call  emit-headers/disp32
+1657     # . . discard args
+1658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1659 +--  9 lines: #?     # write(2/stderr, "emit-segments\n") ----------------------------------------------------------------------------------------------------
+1668     # emit-segments(in, out, segments, labels)
+1669     # . . push args
+1670     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8       .                # push *(EBP+20)
+1671     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8       .                # push *(EBP+16)
+1672     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1673     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1674     # . . call
+1675     e8/call  emit-segments/disp32
+1676     # . . discard args
+1677     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+1678 $emit-output:end:
+1679     # . epilog
+1680     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1681     5d/pop-to-EBP
+1682     c3/return
+1683 
+1684 emit-segments:  # in : (address buffered-file), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+1685     # pseudocode:
+1686     #   var offset-of-next-instruction = 0
+1687     #   var line = new-stream(512, 1)
+1688     #   line-loop:
+1689     #   while true
+1690     #     clear-stream(line)
+1691     #     read-line-buffered(in, line)
+1692     #     if (line->write == 0) break               # end of file
+1693     #     offset-of-next-instruction += num-bytes(line)
+1694     #     while true
+1695     #       var word-slice = next-word(line)
+1696     #       if slice-empty?(word-slice)             # end of line
+1697     #         break
+1698     #       if slice-starts-with?(word-slice, "#")  # comment
+1699     #         break
+1700     #       if is-label?(word-slice)                # no need for label declarations anymore
+1701     #         goto line-loop                        # don't insert empty lines
+1702     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
+1703     #         goto line-loop                        # don't insert empty lines
+1704     #       if length(word-slice) == 2
+1705     #         write-slice-buffered(out, word-slice)
+1706     #         write-buffered(out, " ")
+1707     #         continue
+1708     #       datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
+1709     #       info = get-slice(labels, datum)
+1710     #       if has-metadata?(word-slice, "imm8")
+1711     #         abort  # label should never go to imm8
+1712     #       else if has-metadata?(word-slice, "imm32")
+1713     #         emit(out, info->address, 4)
+1714     #       else if has-metadata?(word-slice, "disp8")
+1715     #         value = info->offset - offset-of-next-instruction
+1716     #         emit(out, value, 1)
+1717     #       else if has-metadata?(word-slice, "disp32")
+1718     #         value = info->offset - offset-of-next-instruction
+1719     #         emit(out, value, 4)
+1720     #       else
+1721     #         abort
+1722     #     write-buffered(out, "\n")
+1723     #
+1724     # registers:
+1725     #   line: ECX
+1726     #   word-slice: EDX
+1727     #   offset-of-next-instruction: EBX
+1728     #   datum: EDI
+1729     #   info: ESI (inner loop only)
+1730     #   temporaries: EAX, ESI (outer loop)
+1731     #
+1732     # . prolog
+1733     55/push-EBP
+1734     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1735     # . save registers
+1736     50/push-EAX
+1737     51/push-ECX
+1738     52/push-EDX
+1739     53/push-EBX
+1740     56/push-ESI
+1741     57/push-EDI
+1742     # var line/ECX : (address stream byte) = stream(512)
+1743     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
+1744     68/push  0x200/imm32/length
+1745     68/push  0/imm32/read
+1746     68/push  0/imm32/write
+1747     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1748     # var word-slice/EDX = {0, 0}
+1749     68/push  0/imm32/end
+1750     68/push  0/imm32/start
+1751     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+1752     # var datum/EDI = {0, 0}
+1753     68/push  0/imm32/end
+1754     68/push  0/imm32/start
+1755     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+1756     # offset-of-next-instruction/EBX = 0
+1757     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+1758 $emit-segments:line-loop:
+1759     # clear-stream(line)
+1760     # . . push args
+1761     51/push-ECX
+1762     # . . call
+1763     e8/call  clear-stream/disp32
+1764     # . . discard args
+1765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1766     # read-line-buffered(in, line)
+1767     # . . push args
+1768     51/push-ECX
+1769     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1770     # . . call
+1771     e8/call  read-line-buffered/disp32
+1772     # . . discard args
+1773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1774 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+1807 $emit-segments:check0:
+1808     # if (line->write == 0) break
+1809     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
+1810     0f 84/jump-if-equal  $emit-segments:end/disp32
+1811     # offset-of-next-instruction += num-bytes(line)
+1812     # . EAX = num-bytes(line)
+1813     # . . push args
+1814     51/push-ECX
+1815     # . . call
+1816     e8/call  num-bytes/disp32
+1817     # . . discard args
+1818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1819     # . EBX = EAX
+1820     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+1821 $emit-segments:word-loop:
+1822     # next-word(line, word-slice)
+1823     # . . push args
+1824     52/push-EDX
+1825     51/push-ECX
+1826     # . . call
+1827     e8/call  next-word/disp32
+1828     # . . discard args
+1829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1830 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+1863 $emit-segments:check1:
+1864     # if (slice-empty?(word-slice)) break
+1865     # . EAX = slice-empty?(word-slice)
+1866     # . . push args
+1867     52/push-EDX
+1868     # . . call
+1869     e8/call  slice-empty?/disp32
+1870     # . . discard args
+1871     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1872     # . if (EAX != 0) break
+1873     3d/compare-EAX-and  0/imm32
+1874     0f 85/jump-if-not-equal  $emit-segments:next-line/disp32
+1875 $emit-segments:check-for-comment:
+1876     # if (slice-starts-with?(word-slice, "#")) break
+1877     # . start/ESI = word-slice->start
+1878     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *EDX to ESI
+1879     # . c/EAX = *start
+1880     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+1881     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
+1882     # . if (EAX == '#') break
+1883     3d/compare-EAX-and  0x23/imm32/hash
+1884     0f 84/jump-if-equal  $emit-segments:next-line/disp32
+1885 $emit-segments:check-for-label:
+1886     # if is-label?(word-slice) break
+1887     # . EAX = is-label?(word-slice)
+1888     # . . push args
+1889     52/push-EDX
+1890     # . . call
+1891     e8/call  is-label?/disp32
+1892     # . . discard args
+1893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1894     # . if (EAX != 0) break
+1895     3d/compare-EAX-and  0/imm32
+1896     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
+1897 $emit-segments:check-for-segment-header:
+1898     # if (slice-equal?(word-slice, "==")) break
+1899     # . EAX = slice-equal?(word-slice, "==")
+1900     # . . push args
+1901     68/push  "=="/imm32
+1902     52/push-EDX
+1903     # . . call
+1904     e8/call  slice-equal?/disp32
+1905     # . . discard args
+1906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1907     # . if (EAX != 0) break
+1908     3d/compare-EAX-and  0/imm32
+1909     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
+1910 $emit-segments:2-character:
+1911     # if (length(word-slice) != 2) goto next check
+1912     # . EAX = length(word-slice)
+1913     8b/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(EDX+4) to EAX
+1914     2b/subtract                     0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # subtract *EDX from EAX
+1915     # . if (EAX != 2) goto next check
+1916     3d/compare-EAX-and  2/imm32
+1917     75/jump-if-not-equal  $emit-segments:check-metadata/disp8
+1918     # write-slice-buffered(out, word-slice)
+1919     # . . push args
+1920     52/push-EDX
+1921     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1922     # . . call
+1923     e8/call  write-slice-buffered/disp32
+1924     # . . discard args
+1925     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1926     # write-buffered(out, " ")
+1927     # . . push args
+1928     68/push  " "/imm32
+1929     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1930     # . . call
+1931     e8/call  write-buffered/disp32
+1932     # . . discard args
+1933     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1934     # continue
+1935     e9/jump  $emit-segments:word-loop/disp32
+1936 $emit-segments:check-metadata:
+1937     # - if we get here, 'word-slice' must be a label to be looked up
+1938     # datum/EDI = next-token-from-slice(word-slice->start, word-slice->end, "/")
+1939     # . . push args
+1940     57/push-EDI
+1941     68/push  0x2f/imm32/slash
+1942     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
+1943     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
+1944     # . . call
+1945     e8/call  next-token-from-slice/disp32
+1946     # . . discard args
+1947     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+1948 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+1981     # info/ESI = get-slice(labels, datum, row-size=16)
+1982     # . EAX = get-slice(labels, datum, row-size=16)
+1983     # . . push args
+1984     68/push  0x10/imm32/row-size
+1985     57/push-EDI
+1986     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+1987     # . . call
+1988     e8/call  get-slice/disp32
+1989     # . . discard args
+1990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1991     # . ESI = EAX
+1992     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
+1993 $emit-segments:check-for-imm8:
+1994     # if (has-metadata?(word-slice, "imm8")) abort
+1995     # . EAX = has-metadata?(EDX, "imm8")
+1996     # . . push args
+1997     68/push  "imm8"/imm32
+1998     52/push-EDX
+1999     # . . call
+2000     e8/call  has-metadata?/disp32
+2001     # . . discard args
+2002     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2003     # . if (EAX != 0) abort
+2004     3d/compare-EAX-and  0/imm32
+2005     0f 85/jump-if-not-equal  $emit-segments:imm8-abort/disp32
+2006 $emit-segments:check-for-imm32:
+2007     # if (!has-metadata?(word-slice, "imm32")) goto next check
+2008     # . EAX = has-metadata?(EDX, "imm32")
+2009     # . . push args
+2010     68/push  "imm32"/imm32
+2011     52/push-EDX
+2012     # . . call
+2013     e8/call  has-metadata?/disp32
+2014     # . . discard args
+2015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2016     # . if (EAX == 0) goto next check
+2017     3d/compare-EAX-and  0/imm32
+2018     74/jump-if-equal  $emit-segments:check-for-disp8/disp8
+2019 +-- 33 lines: #?     # dump info->address --------------------------------------------------------------------------------------------------------------------
+2052     # emit-hex(out, info->address, 4)
+2053     # . . push args
+2054     68/push  4/imm32
+2055     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           8/disp8         .                 # push *(ESI+8)
+2056     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2057     # . . call
+2058     e8/call  emit-hex/disp32
+2059     # . . discard args
+2060     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2061     # continue
+2062     e9/jump  $emit-segments:word-loop/disp32
+2063 $emit-segments:check-for-disp8:
+2064     # if (!has-metadata?(word-slice, "disp8")) goto next check
+2065     # . EAX = has-metadata?(EDX, "disp8")
+2066     # . . push args
+2067     68/push  "disp8"/imm32
+2068     52/push-EDX
+2069     # . . call
+2070     e8/call  has-metadata?/disp32
+2071     # . . discard args
+2072     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2073     # . if (EAX == 0) goto next check
+2074     3d/compare-EAX-and  0/imm32
+2075     74/jump-if-equal  $emit-segments:check-for-disp32/disp8
+2076     # emit-hex(out, info->offset - offset-of-next-instruction, 1)
+2077     # . . push args
+2078     68/push  1/imm32
+2079     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+2080     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # subtract EBX from EAX
+2081     50/push-EAX
+2082     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2083     # . . call
+2084     e8/call  emit-hex/disp32
+2085     # . . discard args
+2086     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2087     # continue
+2088     e9/jump  $emit-segments:word-loop/disp32
+2089 $emit-segments:check-for-disp32:
+2090     # if (!has-metadata?(word-slice, "disp32")) abort
+2091     # . EAX = has-metadata?(EDX, "disp32")
+2092     # . . push args
+2093     68/push  "disp32"/imm32
+2094     52/push-EDX
+2095     # . . call
+2096     e8/call  has-metadata?/disp32
+2097     # . . discard args
+2098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2099     # . if (EAX == 0) abort
+2100     3d/compare-EAX-and  0/imm32
+2101     74/jump-if-equal  $emit-segments:abort/disp8
+2102     # emit-hex(out, info->offset - offset-of-next-instruction, 4)
+2103     # . . push args
+2104     68/push  4/imm32
+2105     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+2106     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # subtract EBX from EAX
+2107     50/push-EAX
+2108     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2109     # . . call
+2110     e8/call  emit-hex/disp32
+2111     # . . discard args
+2112     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2113     # continue
+2114     e9/jump  $emit-segments:word-loop/disp32
+2115 $emit-segments:next-line:
+2116     # write-buffered(out, "\n")
+2117     # . . push args
+2118     68/push  Newline/imm32
+2119     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2120     # . . call
+2121     e8/call  write-buffered/disp32
+2122     # . . discard args
+2123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2124     # loop
+2125     e9/jump  $emit-segments:line-loop/disp32
+2126 $emit-segments:end:
+2127     # . reclaim locals
+2128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x21c/imm32       # add to ESP
+2129     # . restore registers
+2130     5f/pop-to-EDI
+2131     5e/pop-to-ESI
+2132     5b/pop-to-EBX
+2133     5a/pop-to-EDX
+2134     59/pop-to-ECX
+2135     58/pop-to-EAX
+2136     # . epilog
+2137     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2138     5d/pop-to-EBP
+2139     c3/return
+2140 
+2141 $emit-segments:imm8-abort:
+2142     # . _write(2/stderr, error)
+2143     # . . push args
+2144     68/push  "emit-segments: unexpected /imm8"/imm32
+2145     68/push  2/imm32/stderr
+2146     # . . call
+2147     e8/call  _write/disp32
+2148     # . . discard args
+2149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2150     # . syscall(exit, 1)
+2151     bb/copy-to-EBX  1/imm32
+2152     b8/copy-to-EAX  1/imm32/exit
+2153     cd/syscall  0x80/imm8
+2154     # never gets here
+2155 
+2156 $emit-segments:abort:
+2157     # print(stderr, "missing metadata in " word-slice)
+2158     # . _write(2/stderr, "missing metadata in word ")
+2159     # . . push args
+2160     68/push  "emit-segments: missing metadata in "/imm32
+2161     68/push  2/imm32/stderr
+2162     # . . call
+2163     e8/call  _write/disp32
+2164     # . . discard args
+2165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2166     # . write-slice-buffered(Stderr, word-slice)
+2167     # . . push args
+2168     52/push-EDX
+2169     68/push  Stderr/imm32
+2170     # . . call
+2171     e8/call  write-slice-buffered/disp32
+2172     # . . discard args
+2173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2174     # . flush(Stderr)
+2175     # . . push args
+2176     68/push  Stderr/imm32
+2177     # . . call
+2178     e8/call  flush/disp32
+2179     # . . discard args
+2180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2181     # . syscall(exit, 1)
+2182     bb/copy-to-EBX  1/imm32
+2183     b8/copy-to-EAX  1/imm32/exit
+2184     cd/syscall  0x80/imm8
+2185     # never gets here
+2186 
+2187 test-emit-segments:
+2188     # input:
+2189     #   in:
+2190     #     == code 0x1000
+2191     #     ab cd ef gh
+2192     #     ij x/imm32
+2193     #     == data 0x2000
+2194     #     00
+2195     #     x:
+2196     #       34
+2197     #   segments:
+2198     #     - 'code': {0x1074, 0, 5}
+2199     #     - 'data': {0x2079, 5, 1}
+2200     #   labels:
+2201     #     - 'l1': {'code', 3, 0x1077}
+2202     #     - 'x': {'data', 1, 0x207a}
+2203     #
+2204     # output:
+2205     #   ab cd ef gh
+2206     #   ij 7a 20 00 00
+2207     #   00
+2208     #   34
+2209     #
+2210     # . prolog
+2211     55/push-EBP
+2212     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2213     # setup
+2214     # . clear-stream(_test-input-stream)
+2215     # . . push args
+2216     68/push  _test-input-stream/imm32
+2217     # . . call
+2218     e8/call  clear-stream/disp32
+2219     # . . discard args
+2220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2221     # . clear-stream(_test-input-buffered-file+4)
+2222     # . . push args
+2223     b8/copy-to-EAX  _test-input-buffered-file/imm32
+2224     05/add-to-EAX  4/imm32
+2225     50/push-EAX
+2226     # . . call
+2227     e8/call  clear-stream/disp32
+2228     # . . discard args
+2229     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2230     # . clear-stream(_test-output-stream)
+2231     # . . push args
+2232     68/push  _test-output-stream/imm32
+2233     # . . call
+2234     e8/call  clear-stream/disp32
+2235     # . . discard args
+2236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2237     # . clear-stream(_test-output-buffered-file+4)
+2238     # . . push args
+2239     b8/copy-to-EAX  _test-output-buffered-file/imm32
+2240     05/add-to-EAX  4/imm32
+2241     50/push-EAX
+2242     # . . call
+2243     e8/call  clear-stream/disp32
+2244     # . . discard args
+2245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2246     # . var segments/ECX = stream(10 * 16)
+2247     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
+2248     68/push  0xa0/imm32/length
+2249     68/push  0/imm32/read
+2250     68/push  0/imm32/write
+2251     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2252     # . var labels/EDX = stream(512 * 16)
+2253     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
+2254     68/push  0x2000/imm32/length
+2255     68/push  0/imm32/read
+2256     68/push  0/imm32/write
+2257     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+2258     # initialize input
+2259     # . write(_test-input-stream, "== code 0x1000\n")
+2260     # . . push args
+2261     68/push  "== code 0x1000\n"/imm32
+2262     68/push  _test-input-stream/imm32
+2263     # . . call
+2264     e8/call  write/disp32
+2265     # . . discard args
+2266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2267     # . write(_test-input-stream, "ab cd ef gh\n")
+2268     # . . push args
+2269     68/push  "ab cd ef gh\n"/imm32
+2270     68/push  _test-input-stream/imm32
+2271     # . . call
+2272     e8/call  write/disp32
+2273     # . . discard args
+2274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2275     # . write(_test-input-stream, "ij x/imm32\n")
+2276     # . . push args
+2277     68/push  "ij x/imm32\n"/imm32
+2278     68/push  _test-input-stream/imm32
+2279     # . . call
+2280     e8/call  write/disp32
+2281     # . . discard args
+2282     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2283     # . write(_test-input-stream, "== data 0x2000\n")
+2284     # . . push args
+2285     68/push  "== data 0x2000\n"/imm32
+2286     68/push  _test-input-stream/imm32
+2287     # . . call
+2288     e8/call  write/disp32
+2289     # . . discard args
+2290     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2291     # . write(_test-input-stream, "00\n")
+2292     # . . push args
+2293     68/push  "00\n"/imm32
+2294     68/push  _test-input-stream/imm32
+2295     # . . call
+2296     e8/call  write/disp32
+2297     # . . discard args
+2298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2299     # . write(_test-input-stream, "x:\n")
+2300     # . . push args
+2301     68/push  "x:\n"/imm32
+2302     68/push  _test-input-stream/imm32
+2303     # . . call
+2304     e8/call  write/disp32
+2305     # . . discard args
+2306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2307     # . write(_test-input-stream, "34\n")
+2308     # . . push args
+2309     68/push  "34\n"/imm32
+2310     68/push  _test-input-stream/imm32
+2311     # . . call
+2312     e8/call  write/disp32
+2313     # . . discard args
+2314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2315     # . stream-add4(segments, "code", 0x1074, 0, 5)
+2316     68/push  5/imm32/segment-size
+2317     68/push  0/imm32/file-offset
+2318     68/push  0x1074/imm32/start-address
+2319     68/push  "code"/imm32/segment-name
+2320     51/push-ECX
+2321     # . . call
+2322     e8/call  stream-add4/disp32
+2323     # . . discard args
+2324     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+2325     # . stream-add4(segments, "data", 0x2079, 5, 1)
+2326     68/push  1/imm32/segment-size
+2327     68/push  5/imm32/file-offset
+2328     68/push  0x2079/imm32/start-address
+2329     68/push  "data"/imm32/segment-name
+2330     51/push-ECX
+2331     # . . call
+2332     e8/call  stream-add4/disp32
+2333     # . . discard args
+2334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+2335     # . stream-add4(labels, "l1", "code", 3, 0x1077)
+2336     68/push  0x1077/imm32/label-address
+2337     68/push  3/imm32/segment-offset
+2338     68/push  "code"/imm32/segment-name
+2339     68/push  "l1"/imm32/label-name
+2340     52/push-EDX
+2341     # . . call
+2342     e8/call  stream-add4/disp32
+2343     # . . discard args
+2344     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+2345     # . stream-add4(labels, "x", "data", 1, 0x207a)
+2346     68/push  0x207a/imm32/label-address
+2347     68/push  1/imm32/segment-offset
+2348     68/push  "data"/imm32/segment-name
+2349     68/push  "x"/imm32/label-name
+2350     52/push-EDX
+2351     # . . call
+2352     e8/call  stream-add4/disp32
+2353     # . . discard args
+2354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+2355     # component under test
+2356     # . emit-segments(_test-input-buffered-file, _test-output-buffered-file, segments, labels)
+2357     # . . push args
+2358     52/push-EDX
+2359     51/push-ECX
+2360     68/push  _test-output-buffered-file/imm32
+2361     68/push  _test-input-buffered-file/imm32
+2362     # . . call
+2363     e8/call  emit-segments/disp32
+2364     # . . discard args
+2365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+2366     # checks
+2367 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+2400     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
+2401     # . . push args
+2402     68/push  "F - test-emit-segments/0"/imm32
+2403     68/push  "ab cd ef gh "/imm32
+2404     68/push  _test-output-stream/imm32
+2405     # . . call
+2406     e8/call  check-next-stream-line-equal/disp32
+2407     # . . discard args
+2408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2409     # . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg)
+2410     # . . push args
+2411     68/push  "F - test-emit-segments/1"/imm32
+2412     68/push  "ij 7a 20 00 00 "/imm32
+2413     68/push  _test-output-stream/imm32
+2414     # . . call
+2415     e8/call  check-next-stream-line-equal/disp32
+2416     # . . discard args
+2417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2418     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
+2419     # . . push args
+2420     68/push  "F - test-emit-segments/2"/imm32
+2421     68/push  "00 "/imm32
+2422     68/push  _test-output-stream/imm32
+2423     # . . call
+2424     e8/call  check-next-stream-line-equal/disp32
+2425     # . . discard args
+2426     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2427     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
+2428     # . . push args
+2429     68/push  "F - test-emit-segments/3"/imm32
+2430     68/push  "34 "/imm32
+2431     68/push  _test-output-stream/imm32
+2432     # . . call
+2433     e8/call  check-next-stream-line-equal/disp32
+2434     # . . discard args
+2435     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2436     # . epilog
+2437     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2438     5d/pop-to-EBP
+2439     c3/return
+2440 
+2441 emit-headers:  # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+2442     # pseudocode:
+2443     #   emit-elf-header(out, segments, labels)
+2444     #   curr-segment = segments->data
+2445     #   max = segments->data + segments->write
+2446     #   while true
+2447     #     if (curr-segment >= max) break
+2448     #     emit-elf-program-header-entry(out, curr-segment)
+2449     #     curr-segment += 16                        # size of a row
+2450     #
+2451     # . prolog
+2452     55/push-EBP
+2453     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2454     # . save registers
+2455     50/push-EAX
+2456     51/push-ECX
+2457 +--  9 lines: #?     # write(2/stderr, "emit-elf-header\n") --------------------------------------------------------------------------------------------------
+2466     # emit-elf-header(out, segments, labels)
+2467     # . . push args
+2468     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+2469     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2470     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2471     # . . call
+2472     e8/call  emit-elf-header/disp32
+2473     # . . discard args
+2474     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2475     # EAX = segments
+2476     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
+2477     # ECX = segments->write
+2478     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2479     # curr-segment/EAX = segments->data
+2480     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy EAX+12 to EAX
+2481     # max/ECX = segments->data + segments->write
+2482     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # add EAX to ECX
+2483 $emit-headers:loop:
+2484     # if (curr-segment >= max) break
+2485     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+2486     0f 83/jump-if-greater-or-equal-unsigned  $emit-headers:end/disp32
+2487 +-- 69 lines: #?     # dump curr-segment->name ---------------------------------------------------------------------------------------------------------------
+2556 +--  9 lines: #?     # write(2/stderr, "emit-segment-header\n") ----------------------------------------------------------------------------------------------
+2565     # emit-elf-program-header-entry(out, curr-segment)
+2566     # . . push args
+2567     50/push-EAX
+2568     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2569     # . . call
+2570     e8/call  emit-elf-program-header-entry/disp32
+2571     # . . discard args
+2572     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2573     # curr-segment += 16                        # size of a row
+2574     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x10/imm32        # add to EAX
+2575     e9/jump  $emit-headers:loop/disp32
+2576 $emit-headers:end:
+2577     # . restore registers
+2578     59/pop-to-ECX
+2579     58/pop-to-EAX
+2580     # . epilog
+2581     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2582     5d/pop-to-EBP
+2583     c3/return
+2584 
+2585 emit-elf-header:  # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+2586     # pseudocode
+2587     #   *Elf_e_entry = get(labels, "Entry")->address
+2588     #   *Elf_e_phnum = segments->write / 20         # size of a row
+2589     #   emit-hex-array(out, Elf_header)
+2590     #
+2591     # . prolog
+2592     55/push-EBP
+2593     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2594     # . save registers
+2595     50/push-EAX
+2596     51/push-ECX
+2597     52/push-EDX  # just because we need to call idiv
+2598     # *Elf_e_entry = get(labels, "Entry")->address
+2599     # . EAX = labels
+2600     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+2601     # . label-info/EAX = get(labels, "Entry", row-size=16)
+2602     # . . push args
+2603     68/push  0x10/imm32/row-size
+2604     68/push  "Entry"/imm32
+2605     50/push-EAX
+2606     # . . call
+2607     e8/call  get/disp32
+2608     # . . discard args
+2609     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2610     # . EAX = label-info->address
+2611     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EAX+8) to EAX
+2612     # . *Elf_e_entry = EAX
+2613     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_e_entry/disp32                # copy EAX to *Elf_e_entry
+2614     # *Elf_e_phnum = segments->write / 0x20
+2615     # . EAX = segments
+2616     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
+2617     # . len/EAX = segments->write
+2618     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
+2619     # . EAX = len / 0x20  (destroying EDX)
+2620     b9/copy-to-ECX  0x20/imm32
+2621     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
+2622     f7          7/subop/idiv        3/mod/direct    1/rm32/ECX    .           .             .           .           .               .                 # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
+2623     # . *Elf_e_phnum = EAX
+2624     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_e_phnum/disp32                # copy EAX to *Elf_e_phnum
+2625     # emit-hex-array(out, Elf_header)
+2626     # . . push args
+2627     68/push  Elf_header/imm32
+2628     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2629     # . . call
+2630     e8/call  emit-hex-array/disp32
+2631     # . . discard args
+2632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2633 $emit-elf-header:end:
+2634     # . restore registers
+2635     5a/pop-to-EDX
+2636     59/pop-to-ECX
+2637     58/pop-to-EAX
+2638     # . epilog
+2639     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2640     5d/pop-to-EBP
+2641     c3/return
+2642 
+2643 emit-elf-program-header-entry:  # out : (address buffered-file), curr-segment : (address {string, segment-info})
+2644     # pseudocode:
+2645     #   *Elf_p_offset = curr-segment->file-offset
+2646     #   *Elf_p_vaddr = curr-segment->address
+2647     #   *Elf_p_paddr = curr-segment->address
+2648     #   *Elf_p_filesz = curr-segment->size
+2649     #   *Elf_p_memsz = curr-segment->size
+2650     #   if curr-segment->name == "code"
+2651     #     *Elf_p_flags = 5  # r-x
+2652     #   else
+2653     #     *Elf_p_flags = 6  # rw-
+2654     #   emit-hex-array(out, Elf_program_header_entry)
+2655     #
+2656     # . prolog
+2657     55/push-EBP
+2658     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2659     # . save registers
+2660     50/push-EAX
+2661     56/push-ESI
+2662     # ESI = curr-segment
+2663     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+2664     # *Elf_p_offset = curr-segment->file-offset
+2665     # . EAX = curr-segment->file-offset
+2666     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(ESI+8) to EAX
+2667     # . *Elf_p_offset = EAX
+2668     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_offset/disp32               # copy EAX to *Elf_p_offset
+2669     # *Elf_p_vaddr = curr-segment->address
+2670     # . EAX = curr-segment->address
+2671     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+2672     # . *Elf_p_vaddr = EAX
+2673     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_vaddr/disp32                # copy EAX to *Elf_p_vaddr
+2674     # *Elf_p_paddr = curr-segment->address
+2675     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_paddr/disp32                # copy EAX to *Elf_p_paddr
+2676     # *Elf_p_filesz = curr-segment->size
+2677     # . EAX = curr-segment->size
+2678     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(ESI+12) to EAX
+2679     # . *Elf_p_filesz = EAX
+2680     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_filesz/disp32               # copy EAX to *Elf_p_filesz
+2681     # *Elf_p_memsz = curr-segment->size
+2682     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_memsz/disp32                # copy EAX to *Elf_p_memsz
+2683     # if (!string-equal?(curr-segment->name, "code") goto next check
+2684     # . EAX = curr-segment->name
+2685     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+2686     # . EAX = string-equal?(curr-segment->name, "code")
+2687     # . . push args
+2688     68/push  "code"/imm32
+2689     50/push-EAX
+2690     # . . call
+2691     e8/call  string-equal?/disp32
+2692     # . . discard args
+2693     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2694     # . if (EAX == 0) goto next check
+2695     3d/compare-EAX-and  0/imm32
+2696     74/jump-if-equal  $emit-elf-program-header-entry:data/disp8
+2697     # *Elf_p_flags = rw-
+2698     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Elf_p_flags/disp32  6/imm32       # copy to *Elf_p_flags
+2699 $emit-elf-program-header-entry:data:
+2700     # otherwise *Elf_p_flags = r-x
+2701     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Elf_p_flags/disp32  5/imm32       # copy to *Elf_p_flags
+2702     # emit-hex-array(out, Elf_program_header_entry)
+2703     # . . push args
+2704     68/push  Elf_program_header_entry/imm32
+2705     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2706     # . . call
+2707     e8/call  emit-hex-array/disp32
+2708     # . . discard args
+2709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2710 $emit-elf-program-header-entry:end:
+2711     # . restore registers
+2712     5e/pop-to-ESI
+2713     58/pop-to-EAX
+2714     # . epilog
+2715     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2716     5d/pop-to-EBP
+2717     c3/return
+2718 
+2719 # - some helpers for tests
+2720 
+2721 stream-add4:  # in : (address stream byte), key : address, val1 : address, val2 : address, val3 : address
+2722     # . prolog
+2723     55/push-EBP
+2724     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2725     # . save registers
+2726     50/push-EAX
+2727     51/push-ECX
+2728     52/push-EDX
+2729     56/push-ESI
+2730     # ESI = in
+2731     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+2732     # curr/EAX = in->data + in->write
+2733     # . EAX = in->write
+2734     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+2735     # . EAX = ESI+EAX+12
+2736     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
+2737     # max/EDX = in->data + in->length
+2738     # . EDX = in->length
+2739     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(ESI+8) to EDX
+2740     # . EDX = ESI+EDX+12
+2741     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ESI+EDX+12 to EDX
+2742     # if (curr >= max) abort
+2743     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
+2744     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
+2745     # *curr = key
+2746     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
+2747     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
+2748     # curr += 4
+2749     05/add-to-EAX  4/imm32
+2750     # if (curr >= max) abort
+2751     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
+2752     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
+2753     # *curr = val1
+2754     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x10/disp8      .                 # copy *(EBP+16) to ECX
+2755     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
+2756     # curr += 4
+2757     05/add-to-EAX  4/imm32
+2758     # if (curr >= max) abort
+2759     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
+2760     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
+2761     # *curr = val2
+2762     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
+2763     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
+2764     # curr += 4
+2765     05/add-to-EAX  4/imm32
+2766     # if (curr >= max) abort
+2767     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
+2768     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
+2769     # *curr = val3
+2770     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x18/disp8      .                 # copy *(EBP+24) to ECX
+2771     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
+2772     # in->write += 16
+2773     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               0x10/imm32        # add to *ESI
+2774 $stream-add4:end:
+2775     # . restore registers
+2776     5e/pop-to-ESI
+2777     5a/pop-to-EDX
+2778     59/pop-to-ECX
+2779     58/pop-to-EAX
+2780     # . epilog
+2781     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2782     5d/pop-to-EBP
+2783     c3/return
+2784 
+2785 $stream-add4:abort:
+2786     # . _write(2/stderr, error)
+2787     # . . push args
+2788     68/push  "overflow in stream-add4\n"/imm32
+2789     68/push  2/imm32/stderr
+2790     # . . call
+2791     e8/call  _write/disp32
+2792     # . . discard args
+2793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2794     # . syscall(exit, 1)
+2795     bb/copy-to-EBX  1/imm32
+2796     b8/copy-to-EAX  1/imm32/exit
+2797     cd/syscall  0x80/imm8
+2798     # never gets here
+2799 
+2800 # some variants of 'trace' that take multiple arguments in different combinations of types:
+2801 #   n: int
+2802 #   c: character [4-bytes, will eventually be UTF-8]
+2803 #   s: (address string)
+2804 #   l: (address slice)
+2805 # one gotcha: 's5' must not be empty
+2806 
+2807 trace-sssns:  # s1 : (address string), s2 : (address string), s3 : (address string), n4 : int, s5 : (address string)
+2808     # . prolog
+2809     55/push-EBP
+2810     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2811     # write(*Trace-stream, s1)
+2812     # . . push args
+2813     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2814     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2815     # . . call
+2816     e8/call  write/disp32
+2817     # . . discard args
+2818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2819     # write(*Trace-stream, s2)
+2820     # . . push args
+2821     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2822     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2823     # . . call
+2824     e8/call  write/disp32
+2825     # . . discard args
+2826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2827     # write(*Trace-stream, s3)
+2828     # . . push args
+2829     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+2830     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2831     # . . call
+2832     e8/call  write/disp32
+2833     # . . discard args
+2834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2835     # print-int32(*Trace-stream, n4)
+2836     # . . push args
+2837     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+2838     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2839     # . . call
+2840     e8/call  print-int32/disp32
+2841     # . . discard args
+2842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2843     # trace(s5)  # implicitly adds a newline and finalizes the trace line
+2844     # . . push args
+2845     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
+2846     # . . call
+2847     e8/call  trace/disp32
+2848     # . . discard args
+2849     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2850 $trace-sssns:end:
+2851     # . epilog
+2852     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2853     5d/pop-to-EBP
+2854     c3/return
+2855 
+2856 test-trace-sssns:
+2857     # . prolog
+2858     55/push-EBP
+2859     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2860     # setup
+2861     # . *Trace-stream->write = 0
+2862     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
+2863     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
+2864     # trace-sssns("A" "b" "c " 3 " e")
+2865     # . . push args
+2866     68/push  " e"/imm32
+2867     68/push  3/imm32
+2868     68/push  "c "/imm32
+2869     68/push  "b"/imm32
+2870     68/push  "A"/imm32
+2871     # . . call
+2872     e8/call  trace-sssns/disp32
+2873     # . . discard args
+2874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+2875 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+2901     # check-trace-contains("Abc 0x00000003 e")
+2902     # . . push args
+2903     68/push  "F - test-trace-sssns"/imm32
+2904     68/push  "Abc 0x00000003 e"/imm32
+2905     # . . call
+2906     e8/call  check-trace-contains/disp32
+2907     # . . discard args
+2908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2909     # . epilog
+2910     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2911     5d/pop-to-EBP
+2912     c3/return
+2913 
+2914 trace-snsns:  # s1 : (address string), n2 : int, s3 : (address string), n4 : int, s5 : (address string)
+2915     # . prolog
+2916     55/push-EBP
+2917     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2918     # write(*Trace-stream, s1)
+2919     # . . push args
+2920     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2921     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2922     # . . call
+2923     e8/call  write/disp32
+2924     # . . discard args
+2925     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2926     # print-int32(*Trace-stream, n2)
+2927     # . . push args
+2928     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2929     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2930     # . . call
+2931     e8/call  print-int32/disp32
+2932     # . . discard args
+2933     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2934     # write(*Trace-stream, s3)
+2935     # . . push args
+2936     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+2937     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2938     # . . call
+2939     e8/call  write/disp32
+2940     # . . discard args
+2941     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2942     # print-int32(*Trace-stream, n4)
+2943     # . . push args
+2944     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+2945     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+2946     # . . call
+2947     e8/call  print-int32/disp32
+2948     # . . discard args
+2949     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2950     # trace(s5)  # implicitly adds a newline and finalizes the trace line
+2951     # . . push args
+2952     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
+2953     # . . call
+2954     e8/call  trace/disp32
+2955     # . . discard args
+2956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2957 $trace-snsns:end:
+2958     # . epilog
+2959     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2960     5d/pop-to-EBP
+2961     c3/return
+2962 
+2963 test-trace-snsns:
+2964     # . prolog
+2965     55/push-EBP
+2966     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2967     # setup
+2968     # . *Trace-stream->write = 0
+2969     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
+2970     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
+2971     # trace-snsns("A " 2 " c " 3 " e")
+2972     # . . push args
+2973     68/push  " e"/imm32
+2974     68/push  3/imm32
+2975     68/push  " c "/imm32
+2976     68/push  2/imm32
+2977     68/push  "A "/imm32
+2978     # . . call
+2979     e8/call  trace-snsns/disp32
+2980     # . . discard args
+2981     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+2982 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+3008     # check-trace-contains("Abc 0x00000003 e")
+3009     # . . push args
+3010     68/push  "F - test-trace-snsns"/imm32
+3011     68/push  "A 0x00000002 c 0x00000003 e"/imm32
+3012     # . . call
+3013     e8/call  check-trace-contains/disp32
+3014     # . . discard args
+3015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3016     # . epilog
+3017     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3018     5d/pop-to-EBP
+3019     c3/return
+3020 
+3021 trace-slsls:  # s1 : (address string), l2 : (address slice), s3 : (address string), l4 : (address slice), s5 : (address string)
+3022     # . prolog
+3023     55/push-EBP
+3024     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3025     # write(*Trace-stream, s1)
+3026     # . . push args
+3027     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3028     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3029     # . . call
+3030     e8/call  write/disp32
+3031     # . . discard args
+3032     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3033     # write-slice(*Trace-stream, l2)
+3034     # . . push args
+3035     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3036     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3037     # . . call
+3038     e8/call  write-slice/disp32
+3039     # . . discard args
+3040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3041     # write(*Trace-stream, s3)
+3042     # . . push args
+3043     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+3044     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3045     # . . call
+3046     e8/call  write/disp32
+3047     # . . discard args
+3048     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3049     # write-slice(*Trace-stream, l4)
+3050     # . . push args
+3051     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+3052     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3053     # . . call
+3054     e8/call  write-slice/disp32
+3055     # . . discard args
+3056     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3057     # trace(s5)  # implicitly adds a newline and finalizes the trace line
+3058     # . . push args
+3059     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
+3060     # . . call
+3061     e8/call  trace/disp32
+3062     # . . discard args
+3063     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3064 $trace-slsls:end:
+3065     # . epilog
+3066     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3067     5d/pop-to-EBP
+3068     c3/return
+3069 
+3070 test-trace-slsls:
+3071     # . prolog
+3072     55/push-EBP
+3073     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3074     # setup
+3075     # . *Trace-stream->write = 0
+3076     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
+3077     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
+3078     # (EAX..ECX) = "b"
+3079     b8/copy-to-EAX  "b"/imm32
+3080     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3081     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+3082     05/add-to-EAX  4/imm32
+3083     # var b/EBX : (address slice) = {EAX, ECX}
+3084     51/push-ECX
+3085     50/push-EAX
+3086     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
+3087     # (EAX..ECX) = "d"
+3088     b8/copy-to-EAX  "d"/imm32
+3089     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3090     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+3091     05/add-to-EAX  4/imm32
+3092     # var d/EDX : (address slice) = {EAX, ECX}
+3093     51/push-ECX
+3094     50/push-EAX
+3095     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+3096     # trace-slsls("A" b "c" d "e")
+3097     # . . push args
+3098     68/push  "e"/imm32
+3099     52/push-EDX
+3100     68/push  "c"/imm32
+3101     53/push-EBX
+3102     68/push  "A"/imm32
+3103     # . . call
+3104     e8/call  trace-slsls/disp32
+3105     # . . discard args
+3106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+3107 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+3133     # check-trace-contains("Abcde")
+3134     # . . push args
+3135     68/push  "F - test-trace-slsls"/imm32
+3136     68/push  "Abcde"/imm32
+3137     # . . call
+3138     e8/call  check-trace-contains/disp32
+3139     # . . discard args
+3140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3141     # . epilog
+3142     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3143     5d/pop-to-EBP
+3144     c3/return
+3145 
+3146 trace-slsns:  # s1 : (address string), l2 : (address slice), s3 : (address string), n4 : int, s5 : (address string)
+3147     # . prolog
+3148     55/push-EBP
+3149     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3150     # write(*Trace-stream, s1)
+3151     # . . push args
+3152     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3153     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3154     # . . call
+3155     e8/call  write/disp32
+3156     # . . discard args
+3157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3158     # write-slice(*Trace-stream, l2)
+3159     # . . push args
+3160     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3161     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3162     # . . call
+3163     e8/call  write-slice/disp32
+3164     # . . discard args
+3165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3166     # write(*Trace-stream, s3)
+3167     # . . push args
+3168     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+3169     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3170     # . . call
+3171     e8/call  write/disp32
+3172     # . . discard args
+3173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3174     # print-int32(*Trace-stream, n4)
+3175     # . . push args
+3176     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+3177     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3178     # . . call
+3179     e8/call  print-int32/disp32
+3180     # . . discard args
+3181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3182     # trace(s5)  # implicitly adds a newline and finalizes the trace line
+3183     # . . push args
+3184     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
+3185     # . . call
+3186     e8/call  trace/disp32
+3187     # . . discard args
+3188     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3189 $trace-slsns:end:
+3190     # . epilog
+3191     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3192     5d/pop-to-EBP
+3193     c3/return
+3194 
+3195 test-trace-slsns:
+3196     # . prolog
+3197     55/push-EBP
+3198     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3199     # setup
+3200     # . *Trace-stream->write = 0
+3201     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
+3202     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
+3203     # (EAX..ECX) = "b"
+3204     b8/copy-to-EAX  "b"/imm32
+3205     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3206     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+3207     05/add-to-EAX  4/imm32
+3208     # var b/EBX : (address slice) = {EAX, ECX}
+3209     51/push-ECX
+3210     50/push-EAX
+3211     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
+3212     # trace-slsls("A" b "c " 3 " e")
+3213     # . . push args
+3214     68/push  " e"/imm32
+3215     68/push  3/imm32
+3216     68/push  "c "/imm32
+3217     53/push-EBX
+3218     68/push  "A"/imm32
+3219     # . . call
+3220     e8/call  trace-slsns/disp32
+3221     # . . discard args
+3222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+3223 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+3249     # check-trace-contains("Abc 0x00000003 e")
+3250     # . . push args
+3251     68/push  "F - test-trace-slsls"/imm32
+3252     68/push  "Abc 0x00000003 e"/imm32
+3253     # . . call
+3254     e8/call  check-trace-contains/disp32
+3255     # . . discard args
+3256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3257     # . epilog
+3258     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3259     5d/pop-to-EBP
+3260     c3/return
+3261 
+3262 trace-slsss:  # s1 : (address string), l2 : (address slice), s3 : (address string), s4 : (address string), s5 : (address string)
+3263     # . prolog
+3264     55/push-EBP
+3265     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3266     # write(*Trace-stream, s1)
+3267     # . . push args
+3268     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3269     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3270     # . . call
+3271     e8/call  write/disp32
+3272     # . . discard args
+3273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3274     # write-slice(*Trace-stream, l2)
+3275     # . . push args
+3276     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3277     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3278     # . . call
+3279     e8/call  write-slice/disp32
+3280     # . . discard args
+3281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3282     # write(*Trace-stream, s3)
+3283     # . . push args
+3284     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+3285     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3286     # . . call
+3287     e8/call  write/disp32
+3288     # . . discard args
+3289     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3290     # write(*Trace-stream, s4)
+3291     # . . push args
+3292     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+3293     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+3294     # . . call
+3295     e8/call  write/disp32
+3296     # . . discard args
+3297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3298     # trace(s5)  # implicitly adds a newline and finalizes the trace line
+3299     # . . push args
+3300     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
+3301     # . . call
+3302     e8/call  trace/disp32
+3303     # . . discard args
+3304     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3305 $trace-slsss:end:
+3306     # . epilog
+3307     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3308     5d/pop-to-EBP
+3309     c3/return
+3310 
+3311 test-trace-slsss:
+3312     # . prolog
+3313     55/push-EBP
+3314     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3315     # setup
+3316     # . *Trace-stream->write = 0
+3317     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
+3318     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
+3319     # (EAX..ECX) = "b"
+3320     b8/copy-to-EAX  "b"/imm32
+3321     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3322     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
+3323     05/add-to-EAX  4/imm32
+3324     # var b/EBX : (address slice) = {EAX, ECX}
+3325     51/push-ECX
+3326     50/push-EAX
+3327     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
+3328     # trace-slsss("A" b "c" "d" "e")
+3329     # . . push args
+3330     68/push  "e"/imm32
+3331     68/push  "d"/imm32
+3332     68/push  "c"/imm32
+3333     53/push-EBX
+3334     68/push  "A"/imm32
+3335     # . . call
+3336     e8/call  trace-slsss/disp32
+3337     # . . discard args
+3338     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+3339 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
+3365     # check-trace-contains("Abcde")
+3366     # . . push args
+3367     68/push  "F - test-trace-slsss"/imm32
+3368     68/push  "Abcde"/imm32
+3369     # . . call
+3370     e8/call  check-trace-contains/disp32
+3371     # . . discard args
+3372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3373     # . epilog
+3374     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3375     5d/pop-to-EBP
+3376     c3/return
+3377 
+3378 num-bytes:  # line : (address stream) -> EAX : int
+3379     # pseudocode:
+3380     #   result = 0
+3381     #   while true
+3382     #     var word-slice = next-word(line)
+3383     #     if slice-empty?(word-slice)             # end of line
+3384     #       break
+3385     #     if slice-starts-with?(word-slice, "#")  # comment
+3386     #       break
+3387     #     if is-label?(word-slice)                # no need for label declarations anymore
+3388     #       break
+3389     #     if slice-equal?(word-slice, "==")
+3390     #       break                                 # no need for segment header lines
+3391     #     result += compute-width(word-slice)
+3392     #   return result
+3393     #
+3394     # . prolog
+3395     55/push-EBP
+3396     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3397     # . save registers
+3398     51/push-ECX
+3399     52/push-EDX
+3400     53/push-EBX
+3401     # var result/EAX = 0
+3402     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+3403     # var word-slice/ECX = {0, 0}
+3404     68/push  0/imm32/end
+3405     68/push  0/imm32/start
+3406     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+3407 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+3433     # . rewind-stream(line)
+3434     # . . push args
+3435     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3436     # . . call
+3437     e8/call  rewind-stream/disp32
+3438     # . . discard args
+3439     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3440 $num-bytes:loop:
+3441     # next-word(line, word-slice)
+3442     # . . push args
+3443     51/push-ECX
+3444     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3445     # . . call
+3446     e8/call  next-word/disp32
+3447     # . . discard args
+3448     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3449 +-- 46 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+3495 $num-bytes:check0:
+3496     # if (slice-empty?(word-slice)) break
+3497     # . save result
+3498     50/push-EAX
+3499     # . EAX = slice-empty?(word-slice)
+3500     # . . push args
+3501     51/push-ECX
+3502     # . . call
+3503     e8/call  slice-empty?/disp32
+3504     # . . discard args
+3505     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3506     # . if (EAX != 0) break
+3507     3d/compare-EAX-and  0/imm32
+3508     # . restore result now that ZF is set
+3509     58/pop-to-EAX
+3510     75/jump-if-not-equal  $num-bytes:end/disp8
+3511 $num-bytes:check-for-comment:
+3512     # if (slice-starts-with?(word-slice, "#")) break
+3513     # . start/EDX = word-slice->start
+3514     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+3515     # . c/EBX = *start
+3516     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+3517     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/BL    .               .                 # copy byte at *EDX to BL
+3518     # . if (EBX == '#') break
+3519     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x23/imm32/hash   # compare EBX
+3520     74/jump-if-equal  $num-bytes:end/disp8
+3521 $num-bytes:check-for-label:
+3522     # if (slice-ends-with?(word-slice, ":")) break
+3523     # . end/EDX = word-slice->end
+3524     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
+3525     # . c/EBX = *(end-1)
+3526     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+3527     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ECX to BL
+3528     # . if (EBX == ':') break
+3529     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x3a/imm32/colon  # compare EBX
+3530     74/jump-if-equal  $num-bytes:end/disp8
+3531 $num-bytes:check-for-segment-header:
+3532     # if (slice-equal?(word-slice, "==")) break
+3533     # . push result
+3534     50/push-EAX
+3535     # . EAX = slice-equal?(word-slice, "==")
+3536     # . . push args
+3537     68/push  "=="/imm32
+3538     51/push-ECX
+3539     # . . call
+3540     e8/call  slice-equal?/disp32
+3541     # . . discard args
+3542     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3543     # . if (EAX != 0) break
+3544     3d/compare-EAX-and  0/imm32
+3545     # . restore result now that ZF is set
+3546     58/pop-to-EAX
+3547     75/jump-if-not-equal  $num-bytes:end/disp8
+3548 $num-bytes:loop-body:
+3549     # result += compute-width-of-slice(word-slice)
+3550     # . copy result to EDX
+3551     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
+3552     # . EAX = compute-width-of-slice(word-slice)
+3553     # . . push args
+3554     51/push-ECX
+3555     # . . call
+3556     e8/call compute-width-of-slice/disp32
+3557     # . . discard args
+3558     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3559     # . EAX += result
+3560     01/add                          3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # add EDX to EAX
+3561     e9/jump  $num-bytes:loop/disp32
+3562 $num-bytes:end:
+3563     # . rewind-stream(line)
+3564     # . . push args
+3565     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3566     # . . call
+3567     e8/call  rewind-stream/disp32
+3568     # . . discard args
+3569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3570     # . reclaim locals
+3571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3572     # . restore registers
+3573     5b/pop-to-EBX
+3574     5a/pop-to-EDX
+3575     59/pop-to-ECX
+3576     # . epilog
+3577     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3578     5d/pop-to-EBP
+3579     c3/return
+3580 
+3581 test-num-bytes-handles-empty-string:
+3582     # if a line starts with '#', return 0
+3583     # . prolog
+3584     55/push-EBP
+3585     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3586     # setup
+3587     # . clear-stream(_test-input-stream)
+3588     # . . push args
+3589     68/push  _test-input-stream/imm32
+3590     # . . call
+3591     e8/call  clear-stream/disp32
+3592     # . . discard args
+3593     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3594     # . clear-stream(_test-output-stream)
+3595     # . . push args
+3596     68/push  _test-output-stream/imm32
+3597     # . . call
+3598     e8/call  clear-stream/disp32
+3599     # . . discard args
+3600     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3601     # no contents in input
+3602     # EAX = num-bytes(_test-input-stream)
+3603     # . . push args
+3604     68/push  _test-input-stream/imm32
+3605     # . . call
+3606     e8/call  num-bytes/disp32
+3607     # . . discard args
+3608     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3609     # check-ints-equal(EAX, 0, msg)
+3610     # . . push args
+3611     68/push  "F - test-num-bytes-handles-empty-string"/imm32
+3612     68/push  0/imm32/true
+3613     50/push-EAX
+3614     # . . call
+3615     e8/call  check-ints-equal/disp32
+3616     # . . discard args
+3617     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3618     # . epilog
+3619     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3620     5d/pop-to-EBP
+3621     c3/return
+3622 
+3623 test-num-bytes-ignores-comments:
+3624     # if a line starts with '#', return 0
+3625     # . prolog
+3626     55/push-EBP
+3627     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3628     # setup
+3629     # . clear-stream(_test-input-stream)
+3630     # . . push args
+3631     68/push  _test-input-stream/imm32
+3632     # . . call
+3633     e8/call  clear-stream/disp32
+3634     # . . discard args
+3635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3636     # . clear-stream(_test-output-stream)
+3637     # . . push args
+3638     68/push  _test-output-stream/imm32
+3639     # . . call
+3640     e8/call  clear-stream/disp32
+3641     # . . discard args
+3642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3643     # initialize input
+3644     # . write(_test-input-stream, "# abcd")
+3645     # . . push args
+3646     68/push  "# abcd"/imm32
+3647     68/push  _test-input-stream/imm32
+3648     # . . call
+3649     e8/call  write/disp32
+3650     # . . discard args
+3651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3652     # EAX = num-bytes(_test-input-stream)
+3653     # . . push args
+3654     68/push  _test-input-stream/imm32
+3655     # . . call
+3656     e8/call  num-bytes/disp32
+3657     # . . discard args
+3658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3659     # check-ints-equal(EAX, 0, msg)
+3660     # . . push args
+3661     68/push  "F - test-num-bytes-ignores-comments"/imm32
+3662     68/push  0/imm32/true
+3663     50/push-EAX
+3664     # . . call
+3665     e8/call  check-ints-equal/disp32
+3666     # . . discard args
+3667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3668     # . epilog
+3669     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3670     5d/pop-to-EBP
+3671     c3/return
+3672 
+3673 test-num-bytes-ignores-labels:
+3674     # if the first word ends with ':', return 0
+3675     # . prolog
+3676     55/push-EBP
+3677     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3678     # setup
+3679     # . clear-stream(_test-input-stream)
+3680     # . . push args
+3681     68/push  _test-input-stream/imm32
+3682     # . . call
+3683     e8/call  clear-stream/disp32
+3684     # . . discard args
+3685     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3686     # . clear-stream(_test-output-stream)
+3687     # . . push args
+3688     68/push  _test-output-stream/imm32
+3689     # . . call
+3690     e8/call  clear-stream/disp32
+3691     # . . discard args
+3692     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3693     # initialize input
+3694     # . write(_test-input-stream, "ab: # cd")
+3695     # . . push args
+3696     68/push  "ab: # cd"/imm32
+3697     68/push  _test-input-stream/imm32
+3698     # . . call
+3699     e8/call  write/disp32
+3700     # . . discard args
+3701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3702     # EAX = num-bytes(_test-input-stream)
+3703     # . . push args
+3704     68/push  _test-input-stream/imm32
+3705     # . . call
+3706     e8/call  num-bytes/disp32
+3707     # . . discard args
+3708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3709     # check-ints-equal(EAX, 0, msg)
+3710     # . . push args
+3711     68/push  "F - test-num-bytes-ignores-labels"/imm32
+3712     68/push  0/imm32/true
+3713     50/push-EAX
+3714     # . . call
+3715     e8/call  check-ints-equal/disp32
+3716     # . . discard args
+3717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3718     # . epilog
+3719     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3720     5d/pop-to-EBP
+3721     c3/return
+3722 
+3723 test-num-bytes-ignores-segment-headers:
+3724     # if the first word is '==', return 0
+3725     # . prolog
+3726     55/push-EBP
+3727     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3728     # setup
+3729     # . clear-stream(_test-input-stream)
+3730     # . . push args
+3731     68/push  _test-input-stream/imm32
+3732     # . . call
+3733     e8/call  clear-stream/disp32
+3734     # . . discard args
+3735     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3736     # . clear-stream(_test-output-stream)
+3737     # . . push args
+3738     68/push  _test-output-stream/imm32
+3739     # . . call
+3740     e8/call  clear-stream/disp32
+3741     # . . discard args
+3742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3743     # initialize input
+3744     # . write(_test-input-stream, "== ab cd")
+3745     # . . push args
+3746     68/push  "== ab cd"/imm32
+3747     68/push  _test-input-stream/imm32
+3748     # . . call
+3749     e8/call  write/disp32
+3750     # . . discard args
+3751     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3752     # EAX = num-bytes(_test-input-stream)
+3753     # . . push args
+3754     68/push  _test-input-stream/imm32
+3755     # . . call
+3756     e8/call  num-bytes/disp32
+3757     # . . discard args
+3758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3759     # check-ints-equal(EAX, 0, msg)
+3760     # . . push args
+3761     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
+3762     68/push  0/imm32/true
+3763     50/push-EAX
+3764     # . . call
+3765     e8/call  check-ints-equal/disp32
+3766     # . . discard args
+3767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3768     # . epilog
+3769     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3770     5d/pop-to-EBP
+3771     c3/return
+3772 
+3773 test-num-bytes-counts-words-by-default:
+3774     # without metadata, count words
+3775     # . prolog
+3776     55/push-EBP
+3777     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3778     # setup
+3779     # . clear-stream(_test-input-stream)
+3780     # . . push args
+3781     68/push  _test-input-stream/imm32
+3782     # . . call
+3783     e8/call  clear-stream/disp32
+3784     # . . discard args
+3785     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3786     # . clear-stream(_test-output-stream)
+3787     # . . push args
+3788     68/push  _test-output-stream/imm32
+3789     # . . call
+3790     e8/call  clear-stream/disp32
+3791     # . . discard args
+3792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3793     # initialize input
+3794     # . write(_test-input-stream, "ab cd ef")
+3795     # . . push args
+3796     68/push  "ab cd ef"/imm32
+3797     68/push  _test-input-stream/imm32
+3798     # . . call
+3799     e8/call  write/disp32
+3800     # . . discard args
+3801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3802     # EAX = num-bytes(_test-input-stream)
+3803     # . . push args
+3804     68/push  _test-input-stream/imm32
+3805     # . . call
+3806     e8/call  num-bytes/disp32
+3807     # . . discard args
+3808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3809     # check-ints-equal(EAX, 3, msg)
+3810     # . . push args
+3811     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
+3812     68/push  3/imm32/true
+3813     50/push-EAX
+3814     # . . call
+3815     e8/call  check-ints-equal/disp32
+3816     # . . discard args
+3817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3818     # . epilog
+3819     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3820     5d/pop-to-EBP
+3821     c3/return
+3822 
+3823 test-num-bytes-ignores-trailing-comment:
+3824     # trailing comments appropriately ignored
+3825     # . prolog
+3826     55/push-EBP
+3827     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3828     # setup
+3829     # . clear-stream(_test-input-stream)
+3830     # . . push args
+3831     68/push  _test-input-stream/imm32
+3832     # . . call
+3833     e8/call  clear-stream/disp32
+3834     # . . discard args
+3835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3836     # . clear-stream(_test-output-stream)
+3837     # . . push args
+3838     68/push  _test-output-stream/imm32
+3839     # . . call
+3840     e8/call  clear-stream/disp32
+3841     # . . discard args
+3842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3843     # initialize input
+3844     # . write(_test-input-stream, "ab cd # ef")
+3845     # . . push args
+3846     68/push  "ab cd # ef"/imm32
+3847     68/push  _test-input-stream/imm32
+3848     # . . call
+3849     e8/call  write/disp32
+3850     # . . discard args
+3851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3852     # EAX = num-bytes(_test-input-stream)
+3853     # . . push args
+3854     68/push  _test-input-stream/imm32
+3855     # . . call
+3856     e8/call  num-bytes/disp32
+3857     # . . discard args
+3858     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3859     # check-ints-equal(EAX, 2, msg)
+3860     # . . push args
+3861     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
+3862     68/push  2/imm32/true
+3863     50/push-EAX
+3864     # . . call
+3865     e8/call  check-ints-equal/disp32
+3866     # . . discard args
+3867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3868     # . epilog
+3869     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3870     5d/pop-to-EBP
+3871     c3/return
+3872 
+3873 test-num-bytes-handles-imm32:
+3874     # if a word has the /imm32 metadata, count it as 4 bytes
+3875     # . prolog
+3876     55/push-EBP
+3877     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3878     # setup
+3879     # . clear-stream(_test-input-stream)
+3880     # . . push args
+3881     68/push  _test-input-stream/imm32
+3882     # . . call
+3883     e8/call  clear-stream/disp32
+3884     # . . discard args
+3885     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3886     # . clear-stream(_test-output-stream)
+3887     # . . push args
+3888     68/push  _test-output-stream/imm32
+3889     # . . call
+3890     e8/call  clear-stream/disp32
+3891     # . . discard args
+3892     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3893     # initialize input
+3894     # . write(_test-input-stream, "ab cd/imm32 ef")
+3895     # . . push args
+3896     68/push  "ab cd/imm32 ef"/imm32
+3897     68/push  _test-input-stream/imm32
+3898     # . . call
+3899     e8/call  write/disp32
+3900     # . . discard args
+3901     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3902     # EAX = num-bytes(_test-input-stream)
+3903     # . . push args
+3904     68/push  _test-input-stream/imm32
+3905     # . . call
+3906     e8/call  num-bytes/disp32
+3907     # . . discard args
+3908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3909     # check-ints-equal(EAX, 6, msg)
+3910     # . . push args
+3911     68/push  "F - test-num-bytes-handles-imm32"/imm32
+3912     68/push  6/imm32/true
+3913     50/push-EAX
+3914     # . . call
+3915     e8/call  check-ints-equal/disp32
+3916     # . . discard args
+3917     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3918     # . epilog
+3919     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3920     5d/pop-to-EBP
+3921     c3/return
+3922 
+3923 == data
+3924 
+3925 Segment-size:
+3926   0x1000/imm32/4KB
+3927 
+3928 # This block of bytes gets copied to the start of the output ELF file, with
+3929 # some fields filled in.
+3930 # http://www.sco.com/developers/gabi/latest/ch4.eheader.html
+3931 Elf_header:
+3932   # - length
+3933   0x34/imm32
+3934   # - data
+3935 $e_ident:
+3936   7f 45/E 4c/L 46/F
+3937   01/32-bit  01/little-endian  01/file-version  00/no-os-extensions
+3938   00 00 00 00 00 00 00 00  # 8 bytes of padding
+3939 $e_type:
+3940   02 00
+3941 $e_machine:
+3942   03 00
+3943 $e_version:
+3944   1/imm32
+3945 Elf_e_entry:
+3946   0x09000000/imm32  # approximate default; must be updated
+3947 $e_phoff:
+3948   0x34/imm32  # offset for the 'program header table' containing segment headers
+3949 $e_shoff:
+3950   0/imm32  # no sections
+3951 $e_flags:
+3952   0/imm32  # unused
+3953 $e_ehsize:
+3954   0x34 00
+3955 $e_phentsize:
+3956   0x20 00
+3957 Elf_e_phnum:
+3958   00 00  # number of segments; must be updated
+3959 $e_shentsize:
+3960   00 00  # no sections
+3961 $e_shnum:
+3962   00 00
+3963 $e_shstrndx:
+3964   00 00
+3965 
+3966 # This block of bytes gets copied after the Elf_header once for each segment.
+3967 # Some fields need filling in each time.
+3968 # https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
+3969 Elf_program_header_entry:
+3970   # - length
+3971   0x20/imm32
+3972   # - data
+3973 $p_type:
+3974   1/imm32/PT_LOAD
+3975 Elf_p_offset:
+3976   0/imm32  # byte offset in the file at which a segment begins; must be updated
+3977 Elf_p_vaddr:
+3978   0/imm32  # starting address to store the segment at before running the program
+3979 Elf_p_paddr:
+3980   0/imm32  # should have same value as Elf_p_vaddr
+3981 Elf_p_filesz:
+3982   0/imm32
+3983 Elf_p_memsz:
+3984   0/imm32  # should have same value as Elf_p_filesz
+3985 Elf_p_flags:
+3986   6/imm32/rw-  # read/write/execute permissions for the segment; must be updated for the code segment
+3987 $p_align:
+3988   # we hold this constant; changing it will require adjusting the way we
+3989   # compute the starting address for each segment
+3990   0x1000/imm32
+3991 
+3992 # . . vim:nowrap:textwidth=0
+
+ + +