about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-05-18 14:18:52 -0700
committerKartik Agaram <vc@akkartik.com>2019-05-18 14:30:50 -0700
commit2a72df4a579599fce556245211952c0da6664329 (patch)
treecc9e9d0d79a91a9c6742bffc88061f85e38f1cdc /subx
parent2d20add2fd7195c5d22e1371c7b5bce132e58afa (diff)
downloadmu-2a72df4a579599fce556245211952c0da6664329.tar.gz
initial skeleton for survey.subx
Start of the final phase needed to implement SubX in SubX:

  $ cat files.subx ... |dquotes |assort |pack |survey |hex > a.elf

survey.subx is responsible for assigning addresses to labels and segments.
Diffstat (limited to 'subx')
-rw-r--r--subx/apps/surveybin0 -> 19660 bytes
-rw-r--r--subx/apps/survey.subx297
2 files changed, 297 insertions, 0 deletions
diff --git a/subx/apps/survey b/subx/apps/survey
new file mode 100644
index 00000000..d09bac99
--- /dev/null
+++ b/subx/apps/survey
Binary files differdiff --git a/subx/apps/survey.subx b/subx/apps/survey.subx
new file mode 100644
index 00000000..9afb6340
--- /dev/null
+++ b/subx/apps/survey.subx
@@ -0,0 +1,297 @@
+# Assign addresses (co-ordinates) to instructions (landmarks) in a program
+# (landscape).
+# Use the addresses assigned to:
+#   a) replace labels
+#   b) add segment headers containing the initial address
+#
+# To build (from the subx/ directory):
+#   $ ./subx translate *.subx apps/survey.subx -o apps/survey
+# 
+# The expected input is a stream of bytes with segment headers, comments and
+# some interspersed labels.
+#   $ cat x
+#   == code 0x1
+#   l1:
+#   aa bb l1/imm8
+#   cc dd l2/disp32
+#   l2:
+#   ee foo/imm32
+#   == data 0x10
+#   foo:
+#     00
+#
+# The output is the stream of bytes without segment headers or label definitions,
+# and with label references replaced with numeric values/displacements.
+#
+#   $ cat x  |./subx run apps/assort
+#   ...ELF header bytes...
+#   # ELF header above will specify that code segment begins at this offset
+#   aa bb nn  # some computed address
+#   cc dd nn nn nn nn  # some computed displacement
+#   ee nn nn nn nn  # some computed address
+#   # ELF header above will specify that data segment begins at this offset
+#   00
+
+== 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:
+    # initialize heap
+    # . Heap = new-segment(64KB)
+    # . . push args
+    68/push  Heap/imm32
+    68/push  0x10000/imm32/64KB
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+
+    # for debugging: run a single test
+#?     e8/call test-convert/disp32
+#?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+#?     eb/jump  $main:end/disp8
+
+    # run tests if necessary, convert stdin if not
+    # . prolog
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # initialize heap
+    # - if argc > 1 and argv[1] == "test", then return run_tests()
+    # . argc > 1
+    81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
+    7e/jump-if-lesser-or-equal  $run-main/disp8
+    # . argv[1] == "test"
+    # . . push args
+    68/push  "test"/imm32
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # . . call
+    e8/call  kernel-string-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . check result
+    3d/compare-EAX-and  1/imm32
+    75/jump-if-not-equal  $run-main/disp8
+    # . run-tests()
+    e8/call  run-tests/disp32
+    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+    eb/jump  $main:end/disp8
+$run-main:
+    # - otherwise convert stdin
+    # var ed/EAX : exit-descriptor
+    81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+    89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
+    # configure ed to really exit()
+    # . ed->target = 0
+    c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+    # return convert(Stdin, 1/stdout, 2/stderr, ed)
+    # . . push args
+    50/push-EAX/ed
+    68/push  Stderr/imm32
+    68/push  Stdout/imm32
+    68/push  Stdin/imm32
+    # . . call
+    e8/call  convert/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+    # . syscall(exit, 0)
+    bb/copy-to-EBX  0/imm32
+$main:end:
+    b8/copy-to-EAX  1/imm32/exit
+    cd/syscall  0x80/imm8
+
+convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # . save registers
+$convert:end:
+    # . reclaim locals
+    # . restore registers
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+## helpers
+
+# TODO: pass in an allocation descriptor
+get-or-insert-segment:  # table : (address stream row), s : (address slice), n : int -> EAX : (address stream)
+    # pseudocode:
+    #   curr = table->data
+    #   max = &table->data[table->write]
+    #   while curr < max
+    #     if slice-equal?(s, *curr)
+    #       return *(curr+4)
+    #     curr += 8
+    #   if table->write < table->length
+    #     *max = slice-to-string(Heap, s)
+    #     result = new-stream(Heap, n, 1)
+    #     *(max+4) = result
+    #     table->write += 8
+    #     return result
+    #   return 0
+    #
+    # . prolog
+    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
+    # ESI = table
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+    # curr/ECX = table->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
+    # max/EDX = table->data + table->write
+    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+    8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
+$get-or-insert-segment:search-loop:
+    # if (curr >= max) break
+    39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+    7d/jump-if-greater-or-equal  $get-or-insert-segment:not-found/disp8
+    # if (slice-equal?(s, *curr)) return *(curr+4)
+    # . EAX = slice-equal?(s, *curr)
+    # . . push args
+    ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+    # . . call
+    e8/call  slice-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . if (EAX != 0) return EAX = *(curr+4)
+    3d/compare-EAX-and  0/imm32
+    74/jump-if-equal  $get-or-insert-segment:mismatch/disp8
+    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+    eb/jump  $get-or-insert-segment:end/disp8
+$get-or-insert-segment:mismatch:
+    # curr += 8
+    81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
+    # loop
+    eb/jump  $get-or-insert-segment:search-loop/disp8
+$get-or-insert-segment:not-found:
+    # result/EAX = 0
+    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+    # if (table->write >= table->length) abort
+    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+    3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
+    7d/jump-if-greater-or-equal  $get-or-insert-segment:abort/disp8
+    # *max = slice-to-string(Heap, s)
+    # . EAX = slice-to-string(Heap, s)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+    68/push  Heap/imm32
+    # . . call
+    e8/call  slice-to-string/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . *max = EAX
+    89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
+    # result/EAX = new-stream(Heap, n, 1)
+    # . . push args
+    68/push  1/imm32
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+    68/push  Heap/imm32
+    # . . call
+    e8/call  new-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # *(max+4) = result
+    89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
+    # table->write += 8
+    81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               8/imm32           # add to *ESI
+$get-or-insert-segment:end:
+    # . restore registers
+    5e/pop-to-ESI
+    5a/pop-to-EDX
+    59/pop-to-ECX
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+$get-or-insert-segment:abort:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "get-or-insert-segment: too many segments"/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
+    # . syscall(exit, 1)
+    bb/copy-to-EBX  1/imm32
+    b8/copy-to-EAX  1/imm32/exit
+    cd/syscall  0x80/imm8
+    # never gets here
+
+== data
+
+Segment-size:
+  0x1000/imm32/4KB
+
+Heap:
+  # curr
+  0/imm32
+  # limit
+  0/imm32
+
+# This block of bytes gets copied to the start of the output ELF file, with
+# some fields filled in.
+# http://www.sco.com/developers/gabi/latest/ch4.eheader.html
+Elf_header:
+$e_ident:
+  7f 45/E 4c/L 46/F
+  01/32-bit  01/little-endian  01/file-version  00/no-os-extensions
+  00 00 00 00 00 00 00 00  # 8 bytes of padding
+$e_type:
+  02 00
+$e_machine:
+  03 00
+$e_version:
+  1/imm32
+Elf_e_entry:
+  0x09000000/imm32  # approximate default; must be updated
+$e_phoff:
+  0x34/imm32  # offset for the 'program header table' containing segment headers
+$e_shoff:
+  0/imm32  # no sections
+$e_flags:
+  0/imm32  # unused
+$e_ehsize:
+  0x34 00
+$e_phentsize:
+  0x20 00
+Elf_e_phnum:
+  00 00  # number of segments; must be updated
+$e_shentsize:
+  00 00  # no sections
+$e_shnum:
+  00 00
+$e_shstrndx:
+  00 00
+
+# This block of bytes gets copied after the Elf_header once for each segment.
+# Some fields need filling in each time.
+# https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
+Elf_program_header_entry:
+$p_type:
+  1/imm32/PT_LOAD
+Elf_p_offset:
+  0/imm32  # byte offset in the file at which a segment begins; must be updated
+Elf_p_vaddr:
+  0/imm32  # starting address to store the segment at before running the program
+Elf_p_paddr:
+  0/imm32  # should have same value as Elf_p_vaddr
+Elf_p_filesz:
+  0/imm32
+Elf_p_memsz:
+  0/imm32  # should have same value as Elf_p_filesz
+Elf_p_flags:
+  6/imm32/rw-  # read/write/execute permissions for the segment; must be updated for the code segment
+Elf_p_align:
+  # we hold this constant; changing it will require adjusting the way we
+  # compute the starting address for each segment
+  0x1000/imm32
+
+# . . vim:nowrap:textwidth=0