4981 - no, go back to 3 phases
Considering how much trouble a merge phase would be (commit 4978), it seems simpler to just add the extra syntax for controlling the entry point of the generated ELF binary. But I wouldn't have noticed this if I hadn't taken the time to write out the commit messages of 4976 and 4978. Even if we happened to already have linked list primitives built, this may still be a good idea considering that I'm saving quite a lot of code in duplicated entrypoints.
This commit is contained in:
parent
6607a30415
commit
8188bbbc94
|
@ -15,17 +15,13 @@ put_new(Help, "syntax",
|
|||
"Each segment starts with a header line: a '==' delimiter followed by the name of\n"
|
||||
"the segment.\n"
|
||||
"\n"
|
||||
"Currently SubX assumes the first segment encountered contains executable code, and\n"
|
||||
"the second contains global variables. By convention we call them 'code' and 'data'\n"
|
||||
"respectively.\n"
|
||||
"The first instruction executed in the resulting binary is always the first\n"
|
||||
"instruction of the first segment.\n"
|
||||
"The first segment contains code and should be called 'code'.\n"
|
||||
"The second segment should be called 'data'.\n"
|
||||
"The resulting binary starts running from the start of the code segment by default.\n"
|
||||
"To start elsewhere in the code segment, define a special label called 'Entry'.\n"
|
||||
"\n"
|
||||
"Segments with the same name get merged together. This rule helps keep functions and\n"
|
||||
"their data close together in .subx files.\n"
|
||||
"Later segments with the same name get their contents *prepended* to earlier ones.\n"
|
||||
"This rule helps each .subx file to put forth a different entrypoint for the binary,\n"
|
||||
"overriding previously loaded files.\n"
|
||||
"\n"
|
||||
"Lines consist of a series of words. Words can contain arbitrary metadata\n"
|
||||
"after a '/', but they can never contain whitespace. Metadata has no effect\n"
|
||||
|
@ -184,7 +180,7 @@ void flush(program& p, vector<line>& lines) {
|
|||
return;
|
||||
}
|
||||
// End flush(p, lines) Special-cases
|
||||
trace(99, "parse") << "flushing to segment" << end();
|
||||
trace(99, "parse") << "flushing segment" << end();
|
||||
p.segments.back().lines.swap(lines);
|
||||
}
|
||||
|
||||
|
@ -228,6 +224,7 @@ typedef void (*transform_fn)(program&);
|
|||
:(before "End Globals")
|
||||
vector<transform_fn> Transform;
|
||||
|
||||
:(code)
|
||||
void transform(program& p) {
|
||||
trace(99, "transform") << "begin" << end();
|
||||
for (int t = 0; t < SIZE(Transform); ++t)
|
||||
|
@ -267,6 +264,7 @@ void load(const program& p) {
|
|||
if (i == 0) End_of_program = addr;
|
||||
}
|
||||
EIP = p.segments.at(0).start;
|
||||
// End Initialize EIP
|
||||
trace(99, "load") << "done" << end();
|
||||
}
|
||||
|
||||
|
|
|
@ -97,13 +97,14 @@ void write_elf_header(ostream& out, const program& p) {
|
|||
// e_version
|
||||
O(0x01); O(0x00); O(0x00); O(0x00);
|
||||
// e_entry
|
||||
int e_entry = p.segments.at(0).start; // convention
|
||||
uint32_t e_entry = p.segments.at(0).start; // convention
|
||||
// Override e_entry
|
||||
emit(e_entry);
|
||||
// e_phoff -- immediately after ELF header
|
||||
int e_phoff = 0x34;
|
||||
uint32_t e_phoff = 0x34;
|
||||
emit(e_phoff);
|
||||
// e_shoff; unused
|
||||
int dummy32 = 0;
|
||||
uint32_t dummy32 = 0;
|
||||
emit(dummy32);
|
||||
// e_flags; unused
|
||||
emit(dummy32);
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
//: Update the parser to handle non-numeric segment name.
|
||||
//:
|
||||
//: We'll also support repeated segments with non-numeric names.
|
||||
//: When we encounter a new reference to an existing segment we'll *prepend*
|
||||
//: the new data to existing data for the segment.
|
||||
|
||||
:(before "End Globals")
|
||||
map</*name*/string, int> Segment_index;
|
||||
|
@ -46,7 +44,7 @@ if (!starts_with(segment_title, "0x")) {
|
|||
out.segments.push_back(segment());
|
||||
}
|
||||
else {
|
||||
trace(99, "parse") << "prepending to segment '" << segment_title << "'" << end();
|
||||
trace(99, "parse") << "appending to segment '" << segment_title << "'" << end();
|
||||
}
|
||||
Currently_parsing_segment_index = get(Segment_index, segment_title);
|
||||
}
|
||||
|
@ -54,9 +52,9 @@ if (!starts_with(segment_title, "0x")) {
|
|||
:(before "End flush(p, lines) Special-cases")
|
||||
if (Currently_parsing_named_segment) {
|
||||
assert(!p.segments.empty());
|
||||
trace(99, "parse") << "flushing to segment" << end();
|
||||
trace(99, "parse") << "flushing segment" << end();
|
||||
vector<line>& curr_segment_data = p.segments.at(Currently_parsing_segment_index).lines;
|
||||
curr_segment_data.insert(curr_segment_data.begin(), lines.begin(), lines.end());
|
||||
curr_segment_data.insert(curr_segment_data.end(), lines.begin(), lines.end());
|
||||
lines.clear();
|
||||
Currently_parsing_named_segment = false;
|
||||
Currently_parsing_segment_index = -1;
|
||||
|
@ -69,17 +67,19 @@ if (Currently_parsing_named_segment) {
|
|||
== code
|
||||
2d/subtract-from-EAX 0xddccbbaa/imm32
|
||||
+parse: new segment 'code'
|
||||
+parse: prepending to segment 'code'
|
||||
+load: 0x09000054 -> 2d
|
||||
+load: 0x09000055 -> aa
|
||||
+load: 0x09000056 -> bb
|
||||
+load: 0x09000057 -> cc
|
||||
+load: 0x09000058 -> dd
|
||||
+load: 0x09000059 -> 05
|
||||
+load: 0x0900005a -> 0a
|
||||
+load: 0x0900005b -> 0b
|
||||
+load: 0x0900005c -> 0c
|
||||
+load: 0x0900005d -> 0d
|
||||
+parse: appending to segment 'code'
|
||||
# first segment
|
||||
+load: 0x09000054 -> 05
|
||||
+load: 0x09000055 -> 0a
|
||||
+load: 0x09000056 -> 0b
|
||||
+load: 0x09000057 -> 0c
|
||||
+load: 0x09000058 -> 0d
|
||||
# second segment
|
||||
+load: 0x09000059 -> 2d
|
||||
+load: 0x0900005a -> aa
|
||||
+load: 0x0900005b -> bb
|
||||
+load: 0x0900005c -> cc
|
||||
+load: 0x0900005d -> dd
|
||||
|
||||
:(scenario error_on_missing_segment_header)
|
||||
% Hide_errors = true;
|
||||
|
|
|
@ -19,6 +19,25 @@
|
|||
//: Later layers may add more conventions partitioning the space of names. But
|
||||
//: the above rules will remain inviolate.
|
||||
|
||||
//: One special label: the address to start running the program at.
|
||||
|
||||
:(scenario entry_label)
|
||||
== 0x1
|
||||
05 0x0d0c0b0a/imm32
|
||||
Entry:
|
||||
05 0x0d0c0b0a/imm32
|
||||
+run: inst: 0x00000006
|
||||
-run: inst: 0x00000001
|
||||
|
||||
:(before "End Globals")
|
||||
uint32_t Entry_address = 0;
|
||||
:(before "End Reset")
|
||||
Entry_address = 0;
|
||||
:(before "End Initialize EIP")
|
||||
if (Entry_address) EIP = Entry_address;
|
||||
:(after "Override e_entry")
|
||||
if (Entry_address) e_entry = Entry_address;
|
||||
|
||||
:(before "End looks_like_hex_int(s) Detectors")
|
||||
if (SIZE(s) == 2) return true;
|
||||
|
||||
|
@ -87,6 +106,8 @@ void rewrite_labels(program& p) {
|
|||
drop_labels(code);
|
||||
if (trace_contains_errors()) return;
|
||||
replace_labels_with_displacements(code, byte_index);
|
||||
if (contains_key(byte_index, "Entry"))
|
||||
Entry_address = code.start + get(byte_index, "Entry");
|
||||
}
|
||||
|
||||
void compute_byte_indices_for_labels(const segment& code, map<string, int32_t>& byte_index) {
|
||||
|
@ -125,7 +146,7 @@ void compute_byte_indices_for_labels(const segment& code, map<string, int32_t>&
|
|||
raise << "'" << to_string(inst) << "': labels can only be the first word in a line.\n" << end();
|
||||
if (Map_file.is_open())
|
||||
Map_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n';
|
||||
if (contains_key(byte_index, label)) {
|
||||
if (contains_key(byte_index, label) && label != "Entry") {
|
||||
raise << "duplicate label '" << label << "'\n" << end();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
# syscall(exit, 0) -- can't test _write just yet
|
||||
Entry: # just exit; can't test _write just yet
|
||||
# . syscall(exit, 0)
|
||||
bb/copy-to-EBX 0/imm32
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# . 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
|
||||
|
||||
# main: (manual test if this is the last file loaded)
|
||||
Entry: # manual test
|
||||
# check-ints-equal(34, 34)
|
||||
# . . push args
|
||||
68/push "error in check-ints-equal"/imm32
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
Entry: # run all tests
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# . 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
|
||||
|
||||
# main: (manual test if this is the last file loaded)
|
||||
Entry: # manual test
|
||||
# EAX = new-segment(0x1000)
|
||||
# . . push args
|
||||
68/push 0x1000/imm32
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
# run-tests()
|
||||
Entry: # run all tests
|
||||
#? e8/call test-compare-equal-strings/disp32
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
|
|
|
@ -14,13 +14,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
clear-stream: # f : (address stream) -> <void>
|
||||
# . prolog
|
||||
55/push-EBP
|
||||
|
|
|
@ -41,14 +41,6 @@ _test-trace-stream:
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
# run-tests()
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
# Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream.
|
||||
# The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
|
||||
initialize-trace-stream:
|
||||
|
|
|
@ -20,13 +20,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
# TODO: come up with a way to signal when a write to disk fails
|
||||
write: # f : fd or (address stream), s : (address array byte) -> <void>
|
||||
# . prolog
|
||||
|
|
|
@ -5,13 +5,12 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
#? Entry: # run a single test, while debugging
|
||||
#? e8/call test-next-stream-line-equal-stops-at-newline/disp32
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
# compare all the data in a stream (ignoring the read pointer)
|
||||
stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boolean
|
||||
|
|
|
@ -37,13 +37,12 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
#? Entry: # run a single test, while debugging
|
||||
#? e8/call test-stop-skips-returns-on-exit/disp32
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
# Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to
|
||||
# the stack.
|
||||
|
|
|
@ -45,13 +45,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
read: # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX
|
||||
# . prolog
|
||||
55/push-EBP
|
||||
|
|
|
@ -31,14 +31,13 @@ Stdin:
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
#? Entry: # run a single test, while debugging
|
||||
#? e8/call test-read-byte-multiple/disp32
|
||||
#? e8/call test-read-byte-refills-buffer/disp32
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
# return next byte value in EAX, with top 3 bytes cleared.
|
||||
# On EOF, return 0xffffffff.
|
||||
|
|
|
@ -5,19 +5,15 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
# manual test
|
||||
#? Entry: # manual test
|
||||
#? # write-stream(stdout, _test-stream2)
|
||||
#? 68/push _test-stream2/imm32
|
||||
#? 68/push 1/imm32/stdout
|
||||
#? e8/call write-stream/disp32
|
||||
# automatic test
|
||||
#? e8/call test-write-stream-single/disp32
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
write-stream: # f : fd or (address stream), s : (address stream) -> <void>
|
||||
# . prolog
|
||||
|
|
|
@ -5,13 +5,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
# write(out, "Error: "+msg+"\n") then stop(ed, 1)
|
||||
error: # ed : (address exit-descriptor), out : fd or (address stream), msg : (address array byte) -> <void>
|
||||
# . prolog
|
||||
|
|
|
@ -27,13 +27,6 @@ Stdout:
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
# Write lower byte of 'n' to 'f'.
|
||||
write-byte: # f : (address buffered-file), n : int -> <void>
|
||||
# . prolog
|
||||
|
|
|
@ -6,13 +6,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
is-hex-int?: # in : (address slice) -> EAX : boolean
|
||||
# . prolog
|
||||
55/push-EBP
|
||||
|
|
|
@ -5,13 +5,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
print-byte: # f : (address buffered-file), n : int -> <void>
|
||||
# . prolog
|
||||
55/push-EBP
|
||||
|
|
|
@ -5,14 +5,13 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
#? Entry: # run a single test, while debugging
|
||||
#? e8/call test-write-buffered/disp32
|
||||
#? e8/call test-write-buffered-with-intermediate-flush/disp32
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void>
|
||||
# pseudocode:
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
# manual test
|
||||
#? Entry: # manual test
|
||||
#? # . 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
|
||||
|
@ -19,12 +18,10 @@
|
|||
#? 68/push Stderr/imm32
|
||||
#? 50/push-EAX
|
||||
#? e8/call error-byte/disp32
|
||||
# automatic test
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# . syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # . syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
# write(out, "Error: "+msg+": "+byte) then stop(ed, 1)
|
||||
error-byte: # ed : (address exit-descriptor), out : (address buffered-file), msg : (address array byte), n : byte -> <void>
|
||||
|
|
|
@ -29,13 +29,6 @@ Heap:
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
# Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr.
|
||||
# If there isn't enough memory before ad->limit, return 0 and leave 'ad' unmodified.
|
||||
allocate: # ad : (address allocation-descriptor), n : int -> address-or-null/EAX
|
||||
|
|
|
@ -5,13 +5,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
new-stream: # ad : (address allocation-descriptor), length : int, elemsize : int -> address/EAX
|
||||
# . prolog
|
||||
55/push-EBP
|
||||
|
|
|
@ -3,13 +3,6 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
# read bytes from 'f' until (and including) a newline and store them into 's'
|
||||
# return true if no data found, false otherwise
|
||||
# just abort if 's' is too small
|
||||
|
|
|
@ -6,14 +6,13 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
#? Entry: # run a single test, while debugging
|
||||
#? e8/call test-slice-starts-with-fails/disp32
|
||||
#? e8/call test-slice-starts-with-single-character/disp32
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
slice-empty?: # s : (address slice) -> EAX : boolean
|
||||
# . prolog
|
||||
|
|
|
@ -3,13 +3,12 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
#? Entry: # run a single test, while debugging
|
||||
#? e8/call test-next-token-from-slice/disp32
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
#? b8/copy-to-EAX 1/imm32/exit
|
||||
#? cd/syscall 0x80/imm8
|
||||
|
||||
# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
|
||||
# on eof return an empty interval
|
||||
|
|
|
@ -278,7 +278,7 @@ distinguish between code and data. Correspondingly, SubX programs consist of a
|
|||
series of segments, each starting with a header line: `==` followed by a name.
|
||||
The first segment must be named `code`; the second must be named `data`.
|
||||
|
||||
Execution always begins at the start of the `code` segment.
|
||||
Execution begins at the start of the `code` segment by default.
|
||||
|
||||
You can reuse segment names:
|
||||
|
||||
|
@ -293,17 +293,9 @@ You can reuse segment names:
|
|||
...C...
|
||||
```
|
||||
|
||||
The code segment now contains the instructions of `A` as well as `C`. `C`
|
||||
comes _before_ `A`. (Why this order? I'd like to organize SubX programs into
|
||||
sequences of [_layers_](http://akkartik.name/post/wart-layers) that permit
|
||||
incrementally building a subset of layers into a working program with a subset
|
||||
of functionality. This organization requires, among other things, letting each
|
||||
layer control the code that runs when the binary starts up. Letting each layer
|
||||
override the starting address would require additional syntax. Instead, I
|
||||
choose to always begin execution at the start of the code segment, but allow
|
||||
layers to prepend to segments.)
|
||||
The `code` segment now contains the instructions of `A` as well as `C`.
|
||||
|
||||
Within the code segment, each line contains a comment, label or instruction.
|
||||
Within the `code` segment, each line contains a comment, label or instruction.
|
||||
Comments start with a `#` and are ignored. Labels should always be the first
|
||||
word on a line, and they end with a `:`.
|
||||
|
||||
|
@ -346,11 +338,18 @@ The latter is mostly useful for `jump` and `call` instructions.
|
|||
|
||||
Functions are defined using labels. By convention, labels internal to functions
|
||||
(that must only be jumped to) start with a `$`. Any other labels must only be
|
||||
called, never jumped to.
|
||||
called, never jumped to. All labels must be unique.
|
||||
|
||||
A special label is `Entry`, which can be used to specify/override the entry
|
||||
point of the program. It doesn't have to be unique, and the latest definition
|
||||
will override earlier ones.
|
||||
|
||||
(The `Entry` label, along with duplicate segment headers, allows programs to
|
||||
be built up incrementally out of multiple _layers_](http://akkartik.name/post/wart-layers).)
|
||||
|
||||
The data segment consists of labels as before and byte values. Referring to
|
||||
data labels in either code segment instructions or data segment values (using
|
||||
the `imm32` metadata either way) yields their address.
|
||||
data labels in either `code` segment instructions or `data` segment values
|
||||
(using the `imm32` metadata either way) yields their address.
|
||||
|
||||
Automatic tests are an important part of SubX, and there's a simple mechanism
|
||||
to provide a test harness: all functions that start with `test-` are called in
|
||||
|
|
Binary file not shown.
|
@ -30,11 +30,12 @@
|
|||
# . 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: # run tests if necessary, call 'compile' if not
|
||||
|
||||
#? # for debugging: run a single test; don't bother setting status code
|
||||
#? e8/call test-get-num-reads-single-digit/disp32
|
||||
#? eb/jump $main:end/disp8
|
||||
|
||||
# main: run tests if necessary, call 'compile' if not
|
||||
# . prolog
|
||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
||||
# - if argc > 1 and argv[1] == "test", then return run_tests()
|
||||
|
|
Binary file not shown.
|
@ -30,11 +30,12 @@
|
|||
# . 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: # run tests if necessary, call 'compile' if not
|
||||
|
||||
#? # for debugging: run a single test; don't bother setting status code
|
||||
#? e8/call test-get-num-reads-single-digit/disp32
|
||||
#? eb/jump $main:end/disp8
|
||||
|
||||
# main: run tests if necessary, call 'compile' if not
|
||||
# . prolog
|
||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
||||
# - if argc > 1 and argv[1] == "test", then return run_tests()
|
||||
|
|
Binary file not shown.
|
@ -18,7 +18,12 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
Entry: # run tests if necessary, compute `factorial(5)` if not
|
||||
|
||||
#? # for debugging: run a single test; don't bother setting status code
|
||||
#? e8/call test-get-num-reads-single-digit/disp32
|
||||
#? eb/jump $main:end/disp8
|
||||
|
||||
# . prolog
|
||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
||||
# - if argc > 1 and argv[1] == "test", then return run_tests()
|
||||
|
|
BIN
subx/apps/handle
BIN
subx/apps/handle
Binary file not shown.
|
@ -25,12 +25,7 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||
b8/copy-to-EAX 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
# no Entry; the standard library runs all tests by default
|
||||
|
||||
new: # ad : (address allocation-descriptor), n : int, out : (address handle)
|
||||
# . prolog
|
||||
|
|
BIN
subx/apps/hex
BIN
subx/apps/hex
Binary file not shown.
|
@ -17,12 +17,13 @@
|
|||
# . 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
|
||||
|
||||
# for debugging: run a single test
|
||||
Entry: # run tests if necessary, convert stdin if not
|
||||
|
||||
#? # for debugging: run a single test
|
||||
#? e8/call test-skip-until-newline/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
|
||||
|
||||
# main: run tests if necessary, convert stdin if not
|
||||
# . prolog
|
||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
||||
# - if argc > 1 and argv[1] == "test", then return run_tests()
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
# Read a text file of SubX segment 'fragments' with duplicate names, and emit
|
||||
# a list of 'merged' segments.
|
||||
#
|
||||
# Example input:
|
||||
# == A
|
||||
# a
|
||||
# b
|
||||
# c
|
||||
# == B
|
||||
# d
|
||||
# e
|
||||
# == A
|
||||
# f
|
||||
# g
|
||||
# == A
|
||||
# h
|
||||
#
|
||||
# Output:
|
||||
# == A
|
||||
# h
|
||||
# f
|
||||
# g
|
||||
# a
|
||||
# b
|
||||
# c
|
||||
# == B
|
||||
# d
|
||||
# e
|
||||
#
|
||||
# The output gives each segment the contents of all its fragments, with later
|
||||
# fragments *prepended* to earlier ones.
|
||||
#
|
||||
# Prepending necessitates buffering output until the end. We'll convert
|
||||
# fragments to distinct streams, maintain each segment as a linked list of
|
||||
# fragments that's easy to prepend to, and finally emit the linked lists in
|
||||
# order.
|
BIN
subx/apps/pack
BIN
subx/apps/pack
Binary file not shown.
|
@ -20,12 +20,13 @@
|
|||
# . 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
|
||||
|
||||
# for debugging: run a single test
|
||||
Entry: # run tests if necessary, convert stdin if not
|
||||
|
||||
#? # for debugging: run a single test
|
||||
#? e8/call test-convert-instruction-passes-labels-through/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
|
||||
|
||||
# main: run tests if necessary, convert stdin if not
|
||||
# . prolog
|
||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
||||
# - if argc > 1 and argv[1] == "test", then return run_tests()
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# . 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
|
||||
|
||||
# main: return argv-equal(argv[1], argv[2])
|
||||
Entry: # return argv-equal(argv[1], argv[2])
|
||||
# At the start of a SubX program:
|
||||
# argc: *ESP
|
||||
# argv[0]: *(ESP+4)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
Entry: # run all tests
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
# syscall(exit, EAX)
|
||||
89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
# . 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
|
||||
|
||||
# main:
|
||||
Entry:
|
||||
|
||||
# allocate x on the stack
|
||||
81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # subtract from ESP
|
||||
|
||||
|
|
Loading…
Reference in New Issue