# Comparing 'regular' size-prefixed strings. == code # instruction effective address register displacement immediate # . op subop mod rm32 base index scale r32 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes string-equal?: # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean # pseudocode: # if (s->size != benchmark->size) return false # return string-starts-with?(s, benchmark) # # . prologue 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 51/push-ecx 56/push-esi 57/push-edi # esi = s 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi # edi = benchmark 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi # ecx = s->size 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx $string-equal?:sizes: # if (ecx != benchmark->size) return false 39/compare 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # compare *edi and ecx b8/copy-to-eax 0/imm32/false 75/jump-if-!= $string-equal?:end/disp8 $string-equal?:contents: # string-starts-with?(s, benchmark) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call string-starts-with?/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp $string-equal?:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 59/pop-to-ecx # . epilogue 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return string-starts-with?: # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean # pseudocode: # if (s->size < benchmark->size) return false # currs = s->data # currb = benchmark->data # maxb = &benchmark->data[benchmark->size] # while currb < maxb # c1 = *currs # c2 = *currb # if (c1 != c2) return false # ++currs, ++currb # return true # # registers: # currs: esi # maxs: ecx # currb: edi # c1: eax # c2: ebx # # . prologue 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 51/push-ecx 52/push-edx 56/push-esi 57/push-edi # esi = s 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi # edi = benchmark 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi # var bsize/ecx: int = benchmark->size 8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx $string-starts-with?:sizes: # if (s->size < bsize) return false 39/compare
This tool is 2 things:

a) An emulator for SubX, the subset of the 32-bit x86 instruction set used by
Mu.

b) A second translator for SubX programs that emits identical binaries to the
self-hosting versions in the parent directory. Having two diverse compilers
(one in a familiar language, one with minimal syscall surface area) that emit
identical binaries should help gain confidence in Mu.

## Running

`bootstrap` currently has the following sub-commands:

- `bootstrap help`: some helpful documentation to have at your fingertips.

- `bootstrap test`: runs all automated tests.

- `bootstrap translate <input files> -o <output ELF binary>`: translates `.subx`
  files into an executable ELF binary.

- `bootstrap run <ELF binary> <args>`: simulates running the ELF binaries emitted
  by `bootstrap translate`. Useful for testing and debugging.

  Remember, not all 32-bit Linux binaries are guaranteed to run. I'm not
  building general infrastructure here for all of the x86 instruction set.
  SubX is about programming with a small, regular subset of 32-bit x86.

## Hacking

This directory is the only C++ code in the project, and has [an unconventional
organization](http://akkartik.name/post/four-repos).
. . . 0xc/imm32 # add to esp $check-strings-equal:end: # . restore registers 58/pop-to-eax # . epilogue 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return # test the helper test-check-strings-equal: # check-strings-equal("Abc", "Abc") # . . push args 68/push "Abc"/imm32 68/push "Abc"/imm32 # . . call e8/call check-strings-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp c3/return # . . vim:nowrap:textwidth=0