This commit is contained in:
Kartik Agaram 2020-01-01 17:23:29 -08:00
parent 1b050736ee
commit 113bae7311
43 changed files with 89 additions and 98 deletions

View File

@ -27,7 +27,7 @@ put_new(Help, "syntax",
"after a '/', but they can never contain whitespace. Metadata has no effect\n"
"at runtime, but can be handy when rewriting macros.\n"
"\n"
"Check out the examples in the examples/ directory.\n"
"Check out the example programs in the apps/ directory, particularly apps/ex*.\n"
);
:(before "End Help Contents")
cerr << " syntax\n";

View File

@ -48,7 +48,7 @@ In priority order:
You get a thin syntax called SubX for programming in (a subset of) x86 machine
code. (A memory-safe compiled language is [being designed](http://akkartik.name/post/mu-2019-2).)
Here's a program (`examples/ex1.subx`) that returns 42:
Here's a program (`apps/ex1.subx`) that returns 42:
```sh
bb/copy-to-ebx 0x2a/imm32 # 42 in hex
@ -59,8 +59,8 @@ Here's a program (`examples/ex1.subx`) that returns 42:
You can generate tiny zero-dependency ELF binaries with it that run on Linux.
```sh
$ ./subx translate init.linux examples/ex1.subx -o examples/ex1 # on Linux or BSD or Mac
$ ./examples/ex1 # only on Linux
$ ./subx translate init.linux apps/ex1.subx -o apps/ex1 # on Linux or BSD or Mac
$ ./apps/ex1 # only on Linux
$ echo $?
42
```
@ -72,7 +72,7 @@ You can run the generated binaries on an interpreter/VM for better error
messages.
```sh
$ ./subx run examples/ex1 # on Linux or BSD or Mac
$ ./subx run apps/ex1 # on Linux or BSD or Mac
$ echo $?
42
```
@ -80,11 +80,11 @@ messages.
Emulated runs can generate a trace that permits [time-travel debugging](https://github.com/akkartik/mu/blob/master/tools/browse_trace.readme.md).
```sh
$ ./subx --debug translate init.linux examples/factorial.subx -o examples/factorial
$ ./subx --debug translate init.linux apps/factorial.subx -o apps/factorial
saving address->label information to 'labels'
saving address->source information to 'source_lines'
$ ./subx --debug --trace run examples/factorial
$ ./subx --debug --trace run apps/factorial
saving trace to 'last_run'
$ tools/browse_trace last_run # text-mode debugger UI
@ -111,14 +111,14 @@ You can use SubX to translate itself. For example, running natively on Linux:
$ chmod +x hex survey pack assort dquotes tests
# use the generated translator phases to translate SubX programs
$ cat init.linux examples/ex1.subx |./tests |./dquotes |./assort |./pack |./survey |./hex > a.elf
$ cat init.linux apps/ex1.subx |./tests |./dquotes |./assort |./pack |./survey |./hex > a.elf
$ chmod +x a.elf
$ ./a.elf
$ echo $?
42
# or, automating the above steps
$ ./translate_subx init.linux examples/ex1.subx
$ ./translate_subx init.linux apps/ex1.subx
$ ./a.elf
$ echo $?
42
@ -142,7 +142,7 @@ work on a cloud server.)
$ sudo apt install util-linux nasm xorriso # maybe also dosfstools and mtools
# package up a "hello world" program with a third-party kernel into mu_soso.iso
# requires sudo
$ ./gen_soso_iso init.soso examples/ex6.subx
$ ./gen_soso_iso init.soso apps/ex6.subx
# try it out
$ qemu-system-i386 -cdrom mu_soso.iso
```
@ -154,7 +154,7 @@ kernel; that number will gradually go down.)
```sh
$ sudo apt install build-essential flex bison wget libelf-dev libssl-dev xorriso
$ ./gen_linux_iso init.linux examples/ex6.subx
$ ./gen_linux_iso init.linux apps/ex6.subx
$ qemu-system-x86_64 -m 256M -cdrom mu.iso -boot d
```
@ -299,7 +299,7 @@ and digest it:
Here's a more meaty example:
<img alt='examples/ex3.subx' src='html/ex3.png'>
<img alt='apps/ex3.subx' src='html/ex3.png'>
This program sums the first 10 natural numbers. By convention I use horizontal
tabstops to help read instructions, dots to help follow the long lines,
@ -313,8 +313,8 @@ like decimal numbers.
Try running this example now:
```sh
$ ./subx translate init.linux examples/ex3.subx -o examples/ex3
$ ./subx run examples/ex3
$ ./subx translate init.linux apps/ex3.subx -o apps/ex3
$ ./subx run apps/ex3
$ echo $?
55
```
@ -322,7 +322,7 @@ $ echo $?
If you're on Linux you can also run it natively:
```sh
$ ./examples/ex3
$ ./apps/ex3
$ echo $?
55
```
@ -413,13 +413,14 @@ SubX will transparently copy it to the `data` segment and replace it with its
address. Strings are the only place where a SubX word is allowed to contain
spaces.
That should be enough information for writing SubX programs. The `examples/`
directory provides some fodder for practice, giving a more gradual introduction
to SubX features. This repo includes the binary for all examples. At any
commit, an example's binary should be identical bit for bit with the result of
translating the corresponding `.subx` file. The binary should also be natively
runnable on a Linux system running on Intel x86 processors, either 32- or
64-bit. If either of these invariants is broken it's a bug on my part.
That should be enough information for writing SubX programs. The `apps/`
directory provides some fodder for practice in the `apps/ex*` files, giving a
more gradual introduction to SubX features. This repo includes binaries for
all examples. At any commit, an example's binary should be identical bit for
bit with the result of translating the corresponding `.subx` file. The binary
should also be natively runnable on a Linux system running on Intel x86
processors, either 32- or 64-bit. If either of these invariants is broken it's
a bug on my part.
## Running

View File

@ -1,2 +1,11 @@
Larger programs than in the examples/ subdirectory, combining the techniques
demonstrated there.
Some apps written in SubX and Mu, in 3 categories:
* `ex*`: small stand-alone examples that don't need any of the shared code at
the top-level. They each have a simple pedagogical goal. Try these first.
* Code unique to phases of our build toolchain:
* Core SubX: `hex`, `survey`, `pack`, `dquotes`, `assort`, `tests`
* Syntax sugar for SubX: `sigils`, `calls`, `braces`
* More ambitious translator for a memory-safe language (in progress): `mu`
* Miscellaneous test programs.

9
edit
View File

@ -12,12 +12,7 @@ fi
if [[ $EDITOR == *'vim'* ]]
then
LOCAL_SETTINGS='-S vimrc.vim'
fi
if [[ $1 == 'ex'* ]]
then
eval $EDITOR $LOCAL_SETTINGS examples/$1.subx
$EDITOR -S vimrc.vim apps/$1.subx
else
eval $EDITOR $LOCAL_SETTINGS apps/$1.subx
$EDITOR apps/$1.subx
fi

View File

@ -1,6 +0,0 @@
Small example programs, each with a simple pedagogical goal.
They also help to validate SubX instruction semantics against native x86
hardware. For example, loading a single byte to a register would for some time
clear the rest of the register. This behavior was internally consistent with
unit tests. It took running an example binary natively to catch the discrepancy.

104
test_apps
View File

@ -28,144 +28,144 @@ echo "== translating and running using C++"
# example programs
echo ex1
./subx translate init.$OS examples/ex1.subx -o examples/ex1
test "$1" = 'record' || git diff --exit-code examples/ex1
./subx translate init.$OS apps/ex1.subx -o apps/ex1
test "$1" = 'record' || git diff --exit-code apps/ex1
test $EMULATED && {
./subx run examples/ex1 || ret=$?
./subx run apps/ex1 || ret=$?
test $ret -eq 42 # life, the universe and everything
}
test $NATIVE && {
examples/ex1 || ret=$?
apps/ex1 || ret=$?
test $ret -eq 42 # life, the universe and everything
}
echo ex2
./subx translate init.$OS examples/ex2.subx -o examples/ex2
test "$1" = 'record' || git diff --exit-code examples/ex2
./subx translate init.$OS apps/ex2.subx -o apps/ex2
test "$1" = 'record' || git diff --exit-code apps/ex2
test $EMULATED && {
./subx run examples/ex2 || ret=$?
./subx run apps/ex2 || ret=$?
test $ret -eq 2 # 1 + 1
}
test $NATIVE && {
examples/ex2 || ret=$?
apps/ex2 || ret=$?
test $ret -eq 2 # 1 + 1
}
echo ex3
./subx translate init.$OS examples/ex3.subx -o examples/ex3
test "$1" = 'record' || git diff --exit-code examples/ex3
./subx translate init.$OS apps/ex3.subx -o apps/ex3
test "$1" = 'record' || git diff --exit-code apps/ex3
test $EMULATED && {
./subx run examples/ex3 || ret=$?
./subx run apps/ex3 || ret=$?
test $ret -eq 55 # 1 + 2 + ... + 10
}
test $NATIVE && {
examples/ex3 || ret=$?
apps/ex3 || ret=$?
test $ret -eq 55 # 1 + 2 + ... + 10
}
echo ex4
./subx translate init.$OS examples/ex4.subx -o examples/ex4
test "$1" = 'record' || git diff --exit-code examples/ex4
./subx translate init.$OS apps/ex4.subx -o apps/ex4
test "$1" = 'record' || git diff --exit-code apps/ex4
test $EMULATED && {
echo a | ./subx run examples/ex4 >ex4.out || true
echo a | ./subx run apps/ex4 >ex4.out || true
test `cat ex4.out` = 'a'
}
test $NATIVE && {
echo a | examples/ex4 >ex4.out || true
echo a | apps/ex4 >ex4.out || true
test `cat ex4.out` = 'a'
}
echo ex5
./subx translate init.$OS examples/ex5.subx -o examples/ex5
test "$1" = 'record' || git diff --exit-code examples/ex5
./subx translate init.$OS apps/ex5.subx -o apps/ex5
test "$1" = 'record' || git diff --exit-code apps/ex5
test $EMULATED && {
echo a | ./subx run examples/ex5 >ex5.out || true
echo a | ./subx run apps/ex5 >ex5.out || true
test `cat ex5.out` = 'a'
}
test $NATIVE && {
echo a | examples/ex5 >ex5.out || true
echo a | apps/ex5 >ex5.out || true
test `cat ex5.out` = 'a'
}
echo ex6
./subx translate init.$OS examples/ex6.subx -o examples/ex6
test "$1" = 'record' || git diff --exit-code examples/ex6
./subx translate init.$OS apps/ex6.subx -o apps/ex6
test "$1" = 'record' || git diff --exit-code apps/ex6
test $EMULATED && {
./subx run examples/ex6 >ex6.out || true
./subx run apps/ex6 >ex6.out || true
test "`cat ex6.out`" = 'Hello, world!'
}
test $NATIVE && {
examples/ex6 >ex6.out || true
apps/ex6 >ex6.out || true
test "`cat ex6.out`" = 'Hello, world!'
}
echo ex7
./subx translate init.$OS examples/ex7.subx -o examples/ex7
test "$1" = 'record' || git diff --exit-code examples/ex7
./subx translate init.$OS apps/ex7.subx -o apps/ex7
test "$1" = 'record' || git diff --exit-code apps/ex7
test $EMULATED && {
./subx run examples/ex7 || ret=$?
./subx run apps/ex7 || ret=$?
test $ret -eq 97 # 'a'
}
test $NATIVE && {
examples/ex7 || ret=$?
apps/ex7 || ret=$?
test $ret -eq 97 # 'a'
}
echo ex8
./subx translate init.$OS examples/ex8.subx -o examples/ex8
test "$1" = 'record' || git diff --exit-code examples/ex8
./subx translate init.$OS apps/ex8.subx -o apps/ex8
test "$1" = 'record' || git diff --exit-code apps/ex8
test $EMULATED && {
./subx run examples/ex8 abcd || ret=$?
./subx run apps/ex8 abcd || ret=$?
test $ret -eq 4 # length('abcd')
}
test $NATIVE && {
examples/ex8 abcd || ret=$?
apps/ex8 abcd || ret=$?
test $ret -eq 4 # length('abcd')
}
echo ex9
./subx translate init.$OS examples/ex9.subx -o examples/ex9
test "$1" = 'record' || git diff --exit-code examples/ex9
./subx translate init.$OS apps/ex9.subx -o apps/ex9
test "$1" = 'record' || git diff --exit-code apps/ex9
test $EMULATED && {
./subx run examples/ex9 z x || ret=$?
./subx run apps/ex9 z x || ret=$?
test $ret -eq 2 # 'z' - 'x'
}
test $NATIVE && {
examples/ex9 z x || ret=$?
apps/ex9 z x || ret=$?
test $ret -eq 2 # 'z' - 'x'
}
echo ex10
./subx translate init.$OS examples/ex10.subx -o examples/ex10
test "$1" = 'record' || git diff --exit-code examples/ex10
./subx translate init.$OS apps/ex10.subx -o apps/ex10
test "$1" = 'record' || git diff --exit-code apps/ex10
test $EMULATED && {
./subx run examples/ex10 abc abc || ret=$?
./subx run apps/ex10 abc abc || ret=$?
test $ret -eq 1 # equal
./subx run examples/ex10 abc abcd # 0; not equal
./subx run apps/ex10 abc abcd # 0; not equal
}
test $NATIVE && {
examples/ex10 abc abc || ret=$?
apps/ex10 abc abc || ret=$?
test $ret -eq 1 # equal
examples/ex10 abc abcd # 0; not equal
apps/ex10 abc abcd # 0; not equal
}
echo ex11
./subx translate init.$OS examples/ex11.subx -o examples/ex11
test "$1" = 'record' || git diff --exit-code examples/ex11
./subx translate init.$OS apps/ex11.subx -o apps/ex11
test "$1" = 'record' || git diff --exit-code apps/ex11
test $EMULATED && {
./subx run examples/ex11
./subx run apps/ex11
echo
}
test $NATIVE && {
examples/ex11
apps/ex11
echo
}
echo ex12
./subx translate init.$OS examples/ex12.subx -o examples/ex12
test "$1" = 'record' || git diff --exit-code examples/ex12
test $EMULATED && ./subx run examples/ex12 # final byte of mmap'd address is well-nigh guaranteed to be 0
test $NATIVE && examples/ex12
./subx translate init.$OS apps/ex12.subx -o apps/ex12
test "$1" = 'record' || git diff --exit-code apps/ex12
test $EMULATED && ./subx run apps/ex12 # final byte of mmap'd address is well-nigh guaranteed to be 0
test $NATIVE && apps/ex12
# Larger apps that use the standard library.
@ -304,8 +304,8 @@ echo "== translating using SubX (native only)"
for n in `seq 1 12`
do
echo ex$n
./translate_subx init.$OS examples/ex$n.subx
diff examples/ex$n a.elf
./translate_subx init.$OS apps/ex$n.subx
diff apps/ex$n a.elf
done
# Larger apps that use the standard library.

View File

@ -44,15 +44,7 @@ else
endif
function! EditSubx(cmd, arg)
exec "silent! " . a:cmd . " " . SubxPath(a:arg)
endfunction
function! SubxPath(arg)
if a:arg =~ "^ex"
return "examples/" . a:arg . ".subx"
else
return "apps/" . a:arg . ".subx"
endif
exec "silent! " . a:cmd . " apps/" . a:arg . ".subx"
endfunction
" we often want to crib lines of machine code from other files