about summary refs log blame commit diff stats
path: root/apps/ex14.subx
blob: 74dd3809e3e9f919138eae59b5f7dc488443311b (plain) (tree)

























                                                                                                                                                                     
# Multiply 2 numbers.
#
# To run:
#   $ ./bootstrap translate init.linux apps/ex14.subx -o apps/ex14
#   $ ./bootstrap run apps/ex14
# Expected result:
#   $ echo $?
#   6

== code
#   instruction                     effective address                                                   register    displacement    immediate
# . op          subop               mod             rm32          base        index         scale       r32
# . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes

Entry:
    b8/copy-to-eax  1/imm32
    b9/copy-to-ecx  2/imm32
    bb/copy-to-ebx  3/imm32

    69/multiply                     3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx                   3/imm32           # ebx = ecx * 3

$exit:
    # exit(ebx)
    e8/call  syscall_exit/disp32

# . . vim:nowrap:textwidth=0
id='n254' href='#n254'>254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
# Write out the (hex) textual representation of numbers.

== 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

# convert the lowest nibble of eax to ascii and return it in the lowest byte of eax
to-hex-char:  # in/eax: int -> out/eax: int
    # no error checking; accepts argument in eax
    # if (eax <= 9) return eax + '0'
    3d/compare-eax-with  0x9/imm32/9
    7f/jump-if->  $to-hex-char:else/disp8
    05/add-to-eax  0x30/imm32/0
    c3/return
$to-hex-char:else:
    # otherwise return eax + 'a' - 10
    05/add-to-eax  0x57/imm32/a-10
    c3/return

append-byte-hex:  # f: (addr stream byte), n: int
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    # AL = convert upper nibble to hex
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    c1/shift    5/subop/logic-right 3/mod/direct    0/rm32/eax    .           .             .           .           .               4/imm8            # shift eax right by 4 bits, while padding zeroes
    25/and-eax  0xf/imm32
    # . AL = to-hex-char(AL)
    e8/call  to-hex-char/disp32
    # append-byte(f, AL)
    # . . push args
    50/push-eax
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  append-byte/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # AL = convert lower nibble to hex
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    25/and-eax  0xf/imm32
    # . AL = to-hex-char(AL)
    e8/call  to-hex-char/disp32
    # append-byte(f, AL)
    # . . push args
    50/push-eax
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  append-byte/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
$append-byte-hex: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-append-byte-hex:
    # - check that append-byte-hex adds the hex textual representation
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # append-byte-hex(_test-stream, 0xa)  # exercises digit, non-digit as well as leading zero
    # . . push args
    68/push  0xa/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  append-byte-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # check-stream-equal(_test-stream, "0a", msg)
    # . . push args
    68/push  "F - test-append-byte-hex"/imm32
    68/push  "0a"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

# print the hex representation for the lowest byte of a number
write-byte-hex-buffered:  # f: (addr buffered-file), n: int
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    # AL = convert upper nibble to hex
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    c1/shift    5/subop/logic-right 3/mod/direct    0/rm32/eax    .           .             .           .           .               4/imm8            # shift eax right by 4 bits, while padding zeroes
    25/and-eax  0xf/imm32
    # . AL = to-hex-char(AL)
    e8/call  to-hex-char/disp32
    # write-byte-buffered(f, AL)
    # . . push args
    50/push-eax
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  write-byte-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # AL = convert lower nibble to hex
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    25/and-eax  0xf/imm32
    # . AL = to-hex-char(AL)
    e8/call  to-hex-char/disp32
    # write-byte-buffered(f, AL)
    # . . push args
    50/push-eax
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  write-byte-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
$write-byte-hex-buffered: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-write-byte-hex-buffered:
    # - check that write-byte-hex-buffered prints the hex textual representation
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream($_test-buffered-file->buffer)
    # . . push args
    68/push  $_test-buffered-file->buffer/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # write-byte-hex-buffered(_test-buffered-file, 0xa)  # exercises digit, non-digit as well as leading zero
    # . . push args
    68/push  0xa/imm32
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  write-byte-hex-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # flush(_test-buffered-file)
    # . . push args
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # check-stream-equal(_test-stream, "0a", msg)
    # . . push args
    68/push  "F - test-write-byte-hex-buffered"/imm32
    68/push  "0a"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

write-int32-hex:  # f: (addr stream byte), n: int
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
$write-int32-hex:hex-prefix:
    # write(f, "0x")
    # . . push args
    68/push  "0x"/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
$write-int32-hex:rest:
    # write-int32-hex-bits(f, n, 32)
    # . . push args
    68/push  0x20/imm32
    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  write-int32-hex-bits/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
$write-int32-hex:end:
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

# print rightmost 'bits' of 'n'
# bits must be multiple of 4
write-int32-hex-bits:  # f: (addr stream byte), n: int, bits: int
    # pseudocode:
    #  bits -= 4
    #  while true
    #    if (bits < 0) break
    #    eax = n >> bits
    #    eax = eax & 0xf
    #    append-byte(f, AL)
    #    bits -= 4
    #
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    51/push-ecx
    # ecx = bits-4
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
$write-int32-hex-bits:loop:
    # if (bits < 0) break
    81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0/imm32           # compare ecx
    7c/jump-if-<  $write-int32-hex-bits:end/disp8
    # eax = n >> bits
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    d3/>>ecx    5/subop/pad-zeroes  3/mod/direct    0/rm32/eax    .           .             .           .           .               .                 # shift eax right by ecx bits, padding zeroes
    # eax = to-hex-char(AL)
    25/and-eax  0xf/imm32
    e8/call  to-hex-char/disp32
    # append-byte(f, AL)
    # . . push args
    50/push-eax
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  append-byte/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # bits -= 4
    81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
    eb/jump  $write-int32-hex-bits:loop/disp8
$write-int32-hex-bits:end:
    # . restore registers
    59/pop-to-ecx
    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-write-int32-hex:
    # - check that write-int32-hex prints the hex textual representation
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # write-int32-hex(_test-stream, 0x8899aa)
    # . . push args
    68/push  0x8899aa/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  write-int32-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # check-stream-equal(_test-stream, "0x008899aa", msg)
    # . . push args
    68/push  "F - test-write-int32-hex"/imm32
    68/push  "0x008899aa"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

write-int32-hex-buffered:  # f: (addr buffered-file), n: int
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
$write-int32-hex-buffered:hex-prefix:
    # write-buffered(f, "0x")
    # . . push args
    68/push  "0x"/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
$write-int32-hex-buffered:rest:
    # write-int32-hex-bits-buffered(f, n, 32)
    # . . push args
    68/push  0x20/imm32
    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  write-int32-hex-bits-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
$write-int32-hex-buffered:end:
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

# print rightmost 'bits' of 'n'
# bits must be multiple of 4
write-int32-hex-bits-buffered:  # f: (addr buffered-file), n: int, bits: int
    # pseudocode:
    #  bits -= 4
    #  while true
    #    if (bits < 0) break
    #    eax = n >> bits
    #    eax = eax & 0xf
    #    write-byte-buffered(f, AL)
    #    bits -= 4
    #
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    51/push-ecx
    # ecx = bits-4
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
$write-int32-hex-bits-buffered:loop:
    # if (bits < 0) break
    81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0/imm32           # compare ecx
    7c/jump-if-<  $write-int32-hex-bits-buffered:end/disp8
    # eax = n >> bits
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    d3/>>ecx    5/subop/pad-zeroes  3/mod/direct    0/rm32/eax    .           .             .           .           .               .                 # shift eax right by ecx bits, padding zeroes
    # eax = to-hex-char(AL)
    25/and-eax  0xf/imm32
    e8/call  to-hex-char/disp32
    # write-byte-buffered(f, AL)
    # . . push args
    50/push-eax
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  write-byte-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # bits -= 4
    81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
    eb/jump  $write-int32-hex-bits-buffered:loop/disp8
$write-int32-hex-bits-buffered:end:
    # . restore registers
    59/pop-to-ecx
    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-write-int32-hex-buffered:
    # - check that write-int32-hex-buffered prints the hex textual representation
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream($_test-buffered-file->buffer)
    # . . push args
    68/push  $_test-buffered-file->buffer/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # write-int32-hex-buffered(_test-buffered-file, 0x8899aa)
    # . . push args
    68/push  0x8899aa/imm32
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  write-int32-hex-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # flush(_test-buffered-file)
    # . . push args
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
#?     # dump line {{{
#?     # . write-stream(2/stderr, line)
#?     # . . push args
#?     68/push  _test-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
#?     # }}}
    # check-stream-equal(_test-stream, "0x008899aa", msg)
    # . . push args
    68/push  "F - test-write-int32-hex-buffered"/imm32
    68/push  "0x008899aa"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

# . . vim:nowrap:textwidth=0