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"
|
"Each segment starts with a header line: a '==' delimiter followed by the name of\n"
|
||||||
"the segment.\n"
|
"the segment.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Currently SubX assumes the first segment encountered contains executable code, and\n"
|
"The first segment contains code and should be called 'code'.\n"
|
||||||
"the second contains global variables. By convention we call them 'code' and 'data'\n"
|
"The second segment should be called 'data'.\n"
|
||||||
"respectively.\n"
|
"The resulting binary starts running from the start of the code segment by default.\n"
|
||||||
"The first instruction executed in the resulting binary is always the first\n"
|
"To start elsewhere in the code segment, define a special label called 'Entry'.\n"
|
||||||
"instruction of the first segment.\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"Segments with the same name get merged together. This rule helps keep functions and\n"
|
"Segments with the same name get merged together. This rule helps keep functions and\n"
|
||||||
"their data close together in .subx files.\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"
|
"\n"
|
||||||
"Lines consist of a series of words. Words can contain arbitrary metadata\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"
|
"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;
|
return;
|
||||||
}
|
}
|
||||||
// End flush(p, lines) Special-cases
|
// 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);
|
p.segments.back().lines.swap(lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +224,7 @@ typedef void (*transform_fn)(program&);
|
||||||
:(before "End Globals")
|
:(before "End Globals")
|
||||||
vector<transform_fn> Transform;
|
vector<transform_fn> Transform;
|
||||||
|
|
||||||
|
:(code)
|
||||||
void transform(program& p) {
|
void transform(program& p) {
|
||||||
trace(99, "transform") << "begin" << end();
|
trace(99, "transform") << "begin" << end();
|
||||||
for (int t = 0; t < SIZE(Transform); ++t)
|
for (int t = 0; t < SIZE(Transform); ++t)
|
||||||
|
@ -267,6 +264,7 @@ void load(const program& p) {
|
||||||
if (i == 0) End_of_program = addr;
|
if (i == 0) End_of_program = addr;
|
||||||
}
|
}
|
||||||
EIP = p.segments.at(0).start;
|
EIP = p.segments.at(0).start;
|
||||||
|
// End Initialize EIP
|
||||||
trace(99, "load") << "done" << end();
|
trace(99, "load") << "done" << end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,13 +97,14 @@ void write_elf_header(ostream& out, const program& p) {
|
||||||
// e_version
|
// e_version
|
||||||
O(0x01); O(0x00); O(0x00); O(0x00);
|
O(0x01); O(0x00); O(0x00); O(0x00);
|
||||||
// e_entry
|
// 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);
|
emit(e_entry);
|
||||||
// e_phoff -- immediately after ELF header
|
// e_phoff -- immediately after ELF header
|
||||||
int e_phoff = 0x34;
|
uint32_t e_phoff = 0x34;
|
||||||
emit(e_phoff);
|
emit(e_phoff);
|
||||||
// e_shoff; unused
|
// e_shoff; unused
|
||||||
int dummy32 = 0;
|
uint32_t dummy32 = 0;
|
||||||
emit(dummy32);
|
emit(dummy32);
|
||||||
// e_flags; unused
|
// e_flags; unused
|
||||||
emit(dummy32);
|
emit(dummy32);
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
//: Update the parser to handle non-numeric segment name.
|
//: Update the parser to handle non-numeric segment name.
|
||||||
//:
|
//:
|
||||||
//: We'll also support repeated segments with non-numeric names.
|
//: 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")
|
:(before "End Globals")
|
||||||
map</*name*/string, int> Segment_index;
|
map</*name*/string, int> Segment_index;
|
||||||
|
@ -46,7 +44,7 @@ if (!starts_with(segment_title, "0x")) {
|
||||||
out.segments.push_back(segment());
|
out.segments.push_back(segment());
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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")
|
:(before "End flush(p, lines) Special-cases")
|
||||||
if (Currently_parsing_named_segment) {
|
if (Currently_parsing_named_segment) {
|
||||||
assert(!p.segments.empty());
|
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;
|
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();
|
lines.clear();
|
||||||
Currently_parsing_named_segment = false;
|
Currently_parsing_named_segment = false;
|
||||||
Currently_parsing_segment_index = -1;
|
Currently_parsing_segment_index = -1;
|
||||||
|
@ -69,17 +67,19 @@ if (Currently_parsing_named_segment) {
|
||||||
== code
|
== code
|
||||||
2d/subtract-from-EAX 0xddccbbaa/imm32
|
2d/subtract-from-EAX 0xddccbbaa/imm32
|
||||||
+parse: new segment 'code'
|
+parse: new segment 'code'
|
||||||
+parse: prepending to segment 'code'
|
+parse: appending to segment 'code'
|
||||||
+load: 0x09000054 -> 2d
|
# first segment
|
||||||
+load: 0x09000055 -> aa
|
+load: 0x09000054 -> 05
|
||||||
+load: 0x09000056 -> bb
|
+load: 0x09000055 -> 0a
|
||||||
+load: 0x09000057 -> cc
|
+load: 0x09000056 -> 0b
|
||||||
+load: 0x09000058 -> dd
|
+load: 0x09000057 -> 0c
|
||||||
+load: 0x09000059 -> 05
|
+load: 0x09000058 -> 0d
|
||||||
+load: 0x0900005a -> 0a
|
# second segment
|
||||||
+load: 0x0900005b -> 0b
|
+load: 0x09000059 -> 2d
|
||||||
+load: 0x0900005c -> 0c
|
+load: 0x0900005a -> aa
|
||||||
+load: 0x0900005d -> 0d
|
+load: 0x0900005b -> bb
|
||||||
|
+load: 0x0900005c -> cc
|
||||||
|
+load: 0x0900005d -> dd
|
||||||
|
|
||||||
:(scenario error_on_missing_segment_header)
|
:(scenario error_on_missing_segment_header)
|
||||||
% Hide_errors = true;
|
% Hide_errors = true;
|
||||||
|
|
|
@ -19,6 +19,25 @@
|
||||||
//: Later layers may add more conventions partitioning the space of names. But
|
//: Later layers may add more conventions partitioning the space of names. But
|
||||||
//: the above rules will remain inviolate.
|
//: 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")
|
:(before "End looks_like_hex_int(s) Detectors")
|
||||||
if (SIZE(s) == 2) return true;
|
if (SIZE(s) == 2) return true;
|
||||||
|
|
||||||
|
@ -87,6 +106,8 @@ void rewrite_labels(program& p) {
|
||||||
drop_labels(code);
|
drop_labels(code);
|
||||||
if (trace_contains_errors()) return;
|
if (trace_contains_errors()) return;
|
||||||
replace_labels_with_displacements(code, byte_index);
|
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) {
|
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();
|
raise << "'" << to_string(inst) << "': labels can only be the first word in a line.\n" << end();
|
||||||
if (Map_file.is_open())
|
if (Map_file.is_open())
|
||||||
Map_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n';
|
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();
|
raise << "duplicate label '" << label << "'\n" << end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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: # just exit; can't test _write just yet
|
||||||
# syscall(exit, 0) -- can't test _write just yet
|
# . syscall(exit, 0)
|
||||||
bb/copy-to-EBX 0/imm32
|
bb/copy-to-EBX 0/imm32
|
||||||
b8/copy-to-EAX 1/imm32/exit
|
b8/copy-to-EAX 1/imm32/exit
|
||||||
cd/syscall 0x80/imm8
|
cd/syscall 0x80/imm8
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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)
|
# check-ints-equal(34, 34)
|
||||||
# . . push args
|
# . . push args
|
||||||
68/push "error in check-ints-equal"/imm32
|
68/push "error in check-ints-equal"/imm32
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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-'.
|
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)
|
# 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
|
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
|
# . 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
|
# . 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)
|
# EAX = new-segment(0x1000)
|
||||||
# . . push args
|
# . . push args
|
||||||
68/push 0x1000/imm32
|
68/push 0x1000/imm32
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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
|
||||||
# run-tests()
|
|
||||||
#? e8/call test-compare-equal-strings/disp32
|
#? 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-'.
|
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)
|
# syscall(exit, Num-test-failures)
|
||||||
|
|
|
@ -14,13 +14,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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>
|
clear-stream: # f : (address stream) -> <void>
|
||||||
# . prolog
|
# . prolog
|
||||||
55/push-EBP
|
55/push-EBP
|
||||||
|
|
|
@ -41,14 +41,6 @@ _test-trace-stream:
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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.
|
# 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)
|
# The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
|
||||||
initialize-trace-stream:
|
initialize-trace-stream:
|
||||||
|
|
|
@ -20,13 +20,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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
|
# 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>
|
write: # f : fd or (address stream), s : (address array byte) -> <void>
|
||||||
# . prolog
|
# . prolog
|
||||||
|
|
|
@ -5,13 +5,12 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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 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)
|
||||||
# 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
|
||||||
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
|
||||||
b8/copy-to-EAX 1/imm32/exit
|
#? cd/syscall 0x80/imm8
|
||||||
cd/syscall 0x80/imm8
|
|
||||||
|
|
||||||
# compare all the data in a stream (ignoring the read pointer)
|
# compare all the data in a stream (ignoring the read pointer)
|
||||||
stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boolean
|
stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boolean
|
||||||
|
|
|
@ -37,13 +37,12 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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 run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
|
||||||
#? e8/call test-stop-skips-returns-on-exit/disp32
|
#? e8/call test-stop-skips-returns-on-exit/disp32
|
||||||
# syscall(exit, Num-test-failures)
|
#? # 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
|
#? 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
|
#? b8/copy-to-EAX 1/imm32/exit
|
||||||
cd/syscall 0x80/imm8
|
#? cd/syscall 0x80/imm8
|
||||||
|
|
||||||
# Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to
|
# Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to
|
||||||
# the stack.
|
# the stack.
|
||||||
|
|
|
@ -45,13 +45,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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
|
read: # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX
|
||||||
# . prolog
|
# . prolog
|
||||||
55/push-EBP
|
55/push-EBP
|
||||||
|
|
|
@ -31,14 +31,13 @@ Stdin:
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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 run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
|
||||||
#? e8/call test-read-byte-multiple/disp32
|
#? e8/call test-read-byte-multiple/disp32
|
||||||
#? e8/call test-read-byte-refills-buffer/disp32
|
#? e8/call test-read-byte-refills-buffer/disp32
|
||||||
# syscall(exit, Num-test-failures)
|
#? # 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
|
#? 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
|
#? b8/copy-to-EAX 1/imm32/exit
|
||||||
cd/syscall 0x80/imm8
|
#? cd/syscall 0x80/imm8
|
||||||
|
|
||||||
# return next byte value in EAX, with top 3 bytes cleared.
|
# return next byte value in EAX, with top 3 bytes cleared.
|
||||||
# On EOF, return 0xffffffff.
|
# On EOF, return 0xffffffff.
|
||||||
|
|
|
@ -5,19 +5,15 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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: # manual test
|
||||||
# manual test
|
|
||||||
#? # write-stream(stdout, _test-stream2)
|
#? # write-stream(stdout, _test-stream2)
|
||||||
#? 68/push _test-stream2/imm32
|
#? 68/push _test-stream2/imm32
|
||||||
#? 68/push 1/imm32/stdout
|
#? 68/push 1/imm32/stdout
|
||||||
#? e8/call write-stream/disp32
|
#? e8/call write-stream/disp32
|
||||||
# automatic test
|
#? # syscall(exit, Num-test-failures)
|
||||||
#? e8/call test-write-stream-single/disp32
|
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
#? b8/copy-to-EAX 1/imm32/exit
|
||||||
# syscall(exit, Num-test-failures)
|
#? cd/syscall 0x80/imm8
|
||||||
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>
|
write-stream: # f : fd or (address stream), s : (address stream) -> <void>
|
||||||
# . prolog
|
# . prolog
|
||||||
|
|
|
@ -5,13 +5,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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)
|
# write(out, "Error: "+msg+"\n") then stop(ed, 1)
|
||||||
error: # ed : (address exit-descriptor), out : fd or (address stream), msg : (address array byte) -> <void>
|
error: # ed : (address exit-descriptor), out : fd or (address stream), msg : (address array byte) -> <void>
|
||||||
# . prolog
|
# . prolog
|
||||||
|
|
|
@ -27,13 +27,6 @@ Stdout:
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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 lower byte of 'n' to 'f'.
|
||||||
write-byte: # f : (address buffered-file), n : int -> <void>
|
write-byte: # f : (address buffered-file), n : int -> <void>
|
||||||
# . prolog
|
# . prolog
|
||||||
|
|
|
@ -6,13 +6,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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
|
is-hex-int?: # in : (address slice) -> EAX : boolean
|
||||||
# . prolog
|
# . prolog
|
||||||
55/push-EBP
|
55/push-EBP
|
||||||
|
|
|
@ -5,13 +5,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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>
|
print-byte: # f : (address buffered-file), n : int -> <void>
|
||||||
# . prolog
|
# . prolog
|
||||||
55/push-EBP
|
55/push-EBP
|
||||||
|
|
|
@ -5,14 +5,13 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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/disp32
|
||||||
#? e8/call test-write-buffered-with-intermediate-flush/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)
|
||||||
# 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
|
||||||
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
|
||||||
b8/copy-to-EAX 1/imm32/exit
|
#? cd/syscall 0x80/imm8
|
||||||
cd/syscall 0x80/imm8
|
|
||||||
|
|
||||||
write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void>
|
write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void>
|
||||||
# pseudocode:
|
# pseudocode:
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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: # manual test
|
||||||
# manual test
|
|
||||||
#? # . var ed/EAX : exit-descriptor
|
#? # . var ed/EAX : exit-descriptor
|
||||||
#? 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP
|
#? 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
|
#? 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX
|
||||||
|
@ -19,12 +18,10 @@
|
||||||
#? 68/push Stderr/imm32
|
#? 68/push Stderr/imm32
|
||||||
#? 50/push-EAX
|
#? 50/push-EAX
|
||||||
#? e8/call error-byte/disp32
|
#? e8/call error-byte/disp32
|
||||||
# automatic test
|
#? # . syscall(exit, Num-test-failures)
|
||||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
||||||
# . syscall(exit, Num-test-failures)
|
#? b8/copy-to-EAX 1/imm32/exit
|
||||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
|
#? cd/syscall 0x80/imm8
|
||||||
b8/copy-to-EAX 1/imm32/exit
|
|
||||||
cd/syscall 0x80/imm8
|
|
||||||
|
|
||||||
# write(out, "Error: "+msg+": "+byte) then stop(ed, 1)
|
# 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>
|
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
|
# . 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
|
# . 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.
|
# 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.
|
# 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
|
allocate: # ad : (address allocation-descriptor), n : int -> address-or-null/EAX
|
||||||
|
|
|
@ -5,13 +5,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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
|
new-stream: # ad : (address allocation-descriptor), length : int, elemsize : int -> address/EAX
|
||||||
# . prolog
|
# . prolog
|
||||||
55/push-EBP
|
55/push-EBP
|
||||||
|
|
|
@ -3,13 +3,6 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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'
|
# read bytes from 'f' until (and including) a newline and store them into 's'
|
||||||
# return true if no data found, false otherwise
|
# return true if no data found, false otherwise
|
||||||
# just abort if 's' is too small
|
# just abort if 's' is too small
|
||||||
|
|
|
@ -6,14 +6,13 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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-fails/disp32
|
||||||
#? e8/call test-slice-starts-with-single-character/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)
|
||||||
# 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
|
||||||
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
|
||||||
b8/copy-to-EAX 1/imm32/exit
|
#? cd/syscall 0x80/imm8
|
||||||
cd/syscall 0x80/imm8
|
|
||||||
|
|
||||||
slice-empty?: # s : (address slice) -> EAX : boolean
|
slice-empty?: # s : (address slice) -> EAX : boolean
|
||||||
# . prolog
|
# . prolog
|
||||||
|
|
|
@ -3,13 +3,12 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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 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)
|
||||||
# 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
|
||||||
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
|
||||||
b8/copy-to-EAX 1/imm32/exit
|
#? cd/syscall 0x80/imm8
|
||||||
cd/syscall 0x80/imm8
|
|
||||||
|
|
||||||
# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
|
# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
|
||||||
# on eof return an empty interval
|
# 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.
|
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`.
|
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:
|
You can reuse segment names:
|
||||||
|
|
||||||
|
@ -293,17 +293,9 @@ You can reuse segment names:
|
||||||
...C...
|
...C...
|
||||||
```
|
```
|
||||||
|
|
||||||
The code segment now contains the instructions of `A` as well as `C`. `C`
|
The `code` segment now contains the instructions of `A` as well as `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.)
|
|
||||||
|
|
||||||
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
|
Comments start with a `#` and are ignored. Labels should always be the first
|
||||||
word on a line, and they end with a `:`.
|
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
|
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
|
(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
|
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
|
data labels in either `code` segment instructions or `data` segment values
|
||||||
the `imm32` metadata either way) yields their address.
|
(using the `imm32` metadata either way) yields their address.
|
||||||
|
|
||||||
Automatic tests are an important part of SubX, and there's a simple mechanism
|
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
|
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
|
# . 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
|
# . 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
|
#? # for debugging: run a single test; don't bother setting status code
|
||||||
#? e8/call test-get-num-reads-single-digit/disp32
|
#? e8/call test-get-num-reads-single-digit/disp32
|
||||||
#? eb/jump $main:end/disp8
|
#? eb/jump $main:end/disp8
|
||||||
|
|
||||||
# main: run tests if necessary, call 'compile' if not
|
|
||||||
# . prolog
|
# . prolog
|
||||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
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()
|
# - 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
|
# . 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
|
# . 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
|
#? # for debugging: run a single test; don't bother setting status code
|
||||||
#? e8/call test-get-num-reads-single-digit/disp32
|
#? e8/call test-get-num-reads-single-digit/disp32
|
||||||
#? eb/jump $main:end/disp8
|
#? eb/jump $main:end/disp8
|
||||||
|
|
||||||
# main: run tests if necessary, call 'compile' if not
|
|
||||||
# . prolog
|
# . prolog
|
||||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
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()
|
# - 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
|
# . 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
|
# . 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
|
# . prolog
|
||||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
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()
|
# - 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
|
# . 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
|
# . 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:
|
# no Entry; the standard library runs all tests by default
|
||||||
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: # ad : (address allocation-descriptor), n : int, out : (address handle)
|
new: # ad : (address allocation-descriptor), n : int, out : (address handle)
|
||||||
# . prolog
|
# . 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
|
# . 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
|
# . 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
|
#? 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
|
#? 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
|
#? eb/jump $main:end/disp8
|
||||||
|
|
||||||
# main: run tests if necessary, convert stdin if not
|
|
||||||
# . prolog
|
# . prolog
|
||||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
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()
|
# - 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
|
# . 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
|
# . 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
|
#? 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
|
#? 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
|
#? eb/jump $main:end/disp8
|
||||||
|
|
||||||
# main: run tests if necessary, convert stdin if not
|
|
||||||
# . prolog
|
# . prolog
|
||||||
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
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()
|
# - if argc > 1 and argv[1] == "test", then return run_tests()
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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:
|
# At the start of a SubX program:
|
||||||
# argc: *ESP
|
# argc: *ESP
|
||||||
# argv[0]: *(ESP+4)
|
# argv[0]: *(ESP+4)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
# . op subop mod rm32 base index scale r32
|
# . 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
|
# . 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-'.
|
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)
|
# syscall(exit, EAX)
|
||||||
89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX
|
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
|
# . 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
|
# . 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
|
# allocate x on the stack
|
||||||
81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # subtract from ESP
|
81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # subtract from ESP
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue