diff --git a/subx/011run.cc b/subx/011run.cc index 61d93186..45a6699e 100644 --- a/subx/011run.cc +++ b/subx/011run.cc @@ -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& 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; +:(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(); } diff --git a/subx/028translate.cc b/subx/028translate.cc index 2104d57b..d4b1a3a5 100644 --- a/subx/028translate.cc +++ b/subx/028translate.cc @@ -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); diff --git a/subx/034compute_segment_address.cc b/subx/034compute_segment_address.cc index d37060a4..2798e040 100644 --- a/subx/034compute_segment_address.cc +++ b/subx/034compute_segment_address.cc @@ -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 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& 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; diff --git a/subx/035labels.cc b/subx/035labels.cc index 5531910a..e86441cf 100644 --- a/subx/035labels.cc +++ b/subx/035labels.cc @@ -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& byte_index) { @@ -125,7 +146,7 @@ void compute_byte_indices_for_labels(const segment& code, map& 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; } diff --git a/subx/050_write.subx b/subx/050_write.subx index 58cfde7c..083adad6 100644 --- a/subx/050_write.subx +++ b/subx/050_write.subx @@ -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 diff --git a/subx/051test.subx b/subx/051test.subx index ceccc076..6f8cda7a 100644 --- a/subx/051test.subx +++ b/subx/051test.subx @@ -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 diff --git a/subx/052kernel-string-equal.subx b/subx/052kernel-string-equal.subx index 47e1e296..55d6c50a 100644 --- a/subx/052kernel-string-equal.subx +++ b/subx/052kernel-string-equal.subx @@ -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 diff --git a/subx/053new-segment.subx b/subx/053new-segment.subx index fd7203b9..1669b097 100644 --- a/subx/053new-segment.subx +++ b/subx/053new-segment.subx @@ -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 diff --git a/subx/054string-equal.subx b/subx/054string-equal.subx index 607a8447..f5818e79 100644 --- a/subx/054string-equal.subx +++ b/subx/054string-equal.subx @@ -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) diff --git a/subx/055stream.subx b/subx/055stream.subx index f5d24876..92049634 100644 --- a/subx/055stream.subx +++ b/subx/055stream.subx @@ -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) -> # . prolog 55/push-EBP diff --git a/subx/056trace.subx b/subx/056trace.subx index 479569a7..f181e1d7 100644 --- a/subx/056trace.subx +++ b/subx/056trace.subx @@ -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: diff --git a/subx/057write.subx b/subx/057write.subx index 2a111dd3..3135003b 100644 --- a/subx/057write.subx +++ b/subx/057write.subx @@ -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) -> # . prolog diff --git a/subx/058stream-equal.subx b/subx/058stream-equal.subx index 203e5415..ee04d4eb 100644 --- a/subx/058stream-equal.subx +++ b/subx/058stream-equal.subx @@ -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 diff --git a/subx/059stop.subx b/subx/059stop.subx index 4478dab3..eca66bcb 100644 --- a/subx/059stop.subx +++ b/subx/059stop.subx @@ -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. diff --git a/subx/060read.subx b/subx/060read.subx index fcf38dde..9f5f7486 100644 --- a/subx/060read.subx +++ b/subx/060read.subx @@ -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 diff --git a/subx/061read-byte.subx b/subx/061read-byte.subx index d3192ce1..26b63e63 100644 --- a/subx/061read-byte.subx +++ b/subx/061read-byte.subx @@ -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. diff --git a/subx/062write-stream.subx b/subx/062write-stream.subx index b273336b..73766fe3 100644 --- a/subx/062write-stream.subx +++ b/subx/062write-stream.subx @@ -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) -> # . prolog diff --git a/subx/063error.subx b/subx/063error.subx index efda8ea3..db460db1 100644 --- a/subx/063error.subx +++ b/subx/063error.subx @@ -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) -> # . prolog diff --git a/subx/064write-byte.subx b/subx/064write-byte.subx index 61ed4197..9f97c336 100644 --- a/subx/064write-byte.subx +++ b/subx/064write-byte.subx @@ -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 -> # . prolog diff --git a/subx/065hex.subx b/subx/065hex.subx index 9096680b..a5a4bdac 100644 --- a/subx/065hex.subx +++ b/subx/065hex.subx @@ -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 diff --git a/subx/066print-byte.subx b/subx/066print-byte.subx index 3335bcbf..39d7793e 100644 --- a/subx/066print-byte.subx +++ b/subx/066print-byte.subx @@ -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 -> # . prolog 55/push-EBP diff --git a/subx/067write-buffered.subx b/subx/067write-buffered.subx index 94ed4f8a..76ae4313 100644 --- a/subx/067write-buffered.subx +++ b/subx/067write-buffered.subx @@ -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) -> # pseudocode: diff --git a/subx/068error-byte.subx b/subx/068error-byte.subx index 1bf77be5..252aff20 100644 --- a/subx/068error-byte.subx +++ b/subx/068error-byte.subx @@ -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 -> diff --git a/subx/069allocate.subx b/subx/069allocate.subx index 7efeb678..fe471ed9 100644 --- a/subx/069allocate.subx +++ b/subx/069allocate.subx @@ -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 diff --git a/subx/070new-stream.subx b/subx/070new-stream.subx index e6f2e0cb..ad6ab68d 100644 --- a/subx/070new-stream.subx +++ b/subx/070new-stream.subx @@ -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 diff --git a/subx/071read-line.subx b/subx/071read-line.subx index 61513aa9..bb0c03be 100644 --- a/subx/071read-line.subx +++ b/subx/071read-line.subx @@ -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 diff --git a/subx/072slice.subx b/subx/072slice.subx index 54049b6d..c8834719 100644 --- a/subx/072slice.subx +++ b/subx/072slice.subx @@ -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 diff --git a/subx/073next-token.subx b/subx/073next-token.subx index 41dd94f3..1d37ba70 100644 --- a/subx/073next-token.subx +++ b/subx/073next-token.subx @@ -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 diff --git a/subx/Readme.md b/subx/Readme.md index f95be08b..8891e8ee 100644 --- a/subx/Readme.md +++ b/subx/Readme.md @@ -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 diff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1 index 99d08ba4..6588d8e7 100755 Binary files a/subx/apps/crenshaw2-1 and b/subx/apps/crenshaw2-1 differ diff --git a/subx/apps/crenshaw2-1.subx b/subx/apps/crenshaw2-1.subx index ad8dad46..8acdde59 100644 --- a/subx/apps/crenshaw2-1.subx +++ b/subx/apps/crenshaw2-1.subx @@ -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() diff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b index ae66b7b4..08c5f45e 100755 Binary files a/subx/apps/crenshaw2-1b and b/subx/apps/crenshaw2-1b differ diff --git a/subx/apps/crenshaw2-1b.subx b/subx/apps/crenshaw2-1b.subx index 044221eb..55689cca 100644 --- a/subx/apps/crenshaw2-1b.subx +++ b/subx/apps/crenshaw2-1b.subx @@ -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() diff --git a/subx/apps/factorial b/subx/apps/factorial index 90193370..d8cd6766 100755 Binary files a/subx/apps/factorial and b/subx/apps/factorial differ diff --git a/subx/apps/factorial.subx b/subx/apps/factorial.subx index 912285e2..8f4bf47d 100644 --- a/subx/apps/factorial.subx +++ b/subx/apps/factorial.subx @@ -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() diff --git a/subx/apps/handle b/subx/apps/handle index 5708c864..fe9e4cce 100755 Binary files a/subx/apps/handle and b/subx/apps/handle differ diff --git a/subx/apps/handle.subx b/subx/apps/handle.subx index 3848eb76..8f82759a 100644 --- a/subx/apps/handle.subx +++ b/subx/apps/handle.subx @@ -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 diff --git a/subx/apps/hex b/subx/apps/hex index dcc77d5c..8ace80be 100755 Binary files a/subx/apps/hex and b/subx/apps/hex differ diff --git a/subx/apps/hex.subx b/subx/apps/hex.subx index 49ca3bd4..4f65d03b 100644 --- a/subx/apps/hex.subx +++ b/subx/apps/hex.subx @@ -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() diff --git a/subx/apps/merge.subx b/subx/apps/merge.subx deleted file mode 100644 index a5ecc987..00000000 --- a/subx/apps/merge.subx +++ /dev/null @@ -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. diff --git a/subx/apps/pack b/subx/apps/pack index b4d04abe..a16daf5b 100755 Binary files a/subx/apps/pack and b/subx/apps/pack differ diff --git a/subx/apps/pack.subx b/subx/apps/pack.subx index 755f3d79..fa383042 100644 --- a/subx/apps/pack.subx +++ b/subx/apps/pack.subx @@ -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() diff --git a/subx/examples/ex10.subx b/subx/examples/ex10.subx index 7e37a54b..7b88f582 100644 --- a/subx/examples/ex10.subx +++ b/subx/examples/ex10.subx @@ -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) diff --git a/subx/examples/ex11.subx b/subx/examples/ex11.subx index 071a855b..a40c68d4 100644 --- a/subx/examples/ex11.subx +++ b/subx/examples/ex11.subx @@ -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 diff --git a/subx/examples/ex5.subx b/subx/examples/ex5.subx index 2e421fdf..f9ad9ea8 100644 --- a/subx/examples/ex5.subx +++ b/subx/examples/ex5.subx @@ -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