diff --git a/Makefile b/Makefile index 0e96dda..ca80717 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ COMMON =\ line_common.c SRCS =\ + clipboard.c \ editor.c \ editor_tests.c \ editor_test_content.c \ diff --git a/README.md b/README.md index 15c90f6..f9233de 100644 --- a/README.md +++ b/README.md @@ -174,4 +174,4 @@ make -j make tests ``` -This will create a folder called tests in the unit folder. Inside this folder are 3 files that need to be copied to the same directory on your spectrum. The unitnext file is a dotn file which needs to be copied to the /dot directory. Then make sure you are in the folder containing the test_bad and test_op files and type .unitnext. +This will create a folder called tests in the unit folder. Inside this folder are 3 files that need to be copied to the same directory on your Next. Enter the folder and then type ../unitnext from the command line to launch the tests. \ No newline at end of file diff --git a/build/48/specasm/Makefile b/build/48/specasm/Makefile index e5821be..64bf7d5 100644 --- a/build/48/specasm/Makefile +++ b/build/48/specasm/Makefile @@ -17,6 +17,7 @@ SPECASM = \ line_parse.o \ line_parse_common.o \ error.o \ + specasm_mainloop.o \ state_base.o \ state_dump.o \ state_parse.o \ diff --git a/build/next/specasm/Make.include b/build/next/specasm/Make.include index d104414..cedd502 100644 --- a/build/next/specasm/Make.include +++ b/build/next/specasm/Make.include @@ -56,6 +56,7 @@ peer_file_next.o: peer_file_next.c error.h peer_file.h util_print_next.o: util_print_next.c util_print_zx.h samake.o: samake.c peer.h error.h peer_zx.h line.h \ salink.h peer_file.h state_base.h strings.h +clipboard_banked.o: clipboard.c clipboard.h line.h error.h %.o: %.c zcc $(CFLAGS) -o $@ -c $< diff --git a/build/next/specasm/Makefile b/build/next/specasm/Makefile index 79cc6d4..8ab870a 100644 --- a/build/next/specasm/Makefile +++ b/build/next/specasm/Makefile @@ -16,6 +16,9 @@ line_dump_banked.o: line_dump.c ld_parse_banked.o: ld_parse.c $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $< +clipboard_banked.o: clipboard.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_46_H --constsegBANK_46_H --datasegBANK_46_H -c $< + expression_banked.o: expression.c $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_43_H --constsegBANK_43_H --datasegBANK_43_H -c $< @@ -30,12 +33,14 @@ include Make.include SPECASM = \ specasm_next.o \ specasm_trampolines.o \ + clipboard_banked.o \ line_common.o \ ld_parse_banked.o \ line_dump_banked.o \ line_dump_common.o \ line_parse_banked.o \ line_parse_common.o \ + specasm_mainloop.o \ error.o \ state_base.o \ state_dump.o \ diff --git a/build/next/unit/Make.include b/build/next/unit/Make.include index 6779ceb..ff9f741 100644 --- a/build/next/unit/Make.include +++ b/build/next/unit/Make.include @@ -41,6 +41,7 @@ peer_file_next.o: peer_file_next.c error.h peer_file.h util_print_next.o: util_print_next.c util_print_zx.h samake.o: samake.c peer.h error.h peer_zx.h line.h \ salink.h peer_file.h state_base.h strings.h +clipboard_banked.o: clipboard.c clipboard.h line.h error.h unittests_zx.o: ../src/unittests_zx.c ../src/error.h ../src/line.h \ ../src/state.h ../src/state_base.h ../src/strings.h ../src/test_content.h ../src/test_content_zx.h \ diff --git a/build/next/unit/Makefile b/build/next/unit/Makefile index a992236..4f30a47 100644 --- a/build/next/unit/Makefile +++ b/build/next/unit/Makefile @@ -16,6 +16,9 @@ line_dump_banked.o: line_dump.c ld_parse_banked.o: ld_parse.c $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $< +clipboard_banked.o: clipboard.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_46_H --constsegBANK_46_H --datasegBANK_46_H -c $< + include Make.include UNITNEXT = \ @@ -23,6 +26,7 @@ UNITNEXT = \ state_base.o \ specasm_trampolines.o \ line_common.o \ + clipboard_banked.o \ ld_parse_banked.o \ line_dump_banked.o \ line_dump_common.o \ diff --git a/docs/specasm.md b/docs/specasm.md index 8aa45de..76e06e9 100644 --- a/docs/specasm.md +++ b/docs/specasm.md @@ -6,6 +6,9 @@ Specasm is a Z80 assembler designed to run on the 48k and 128k ZX Spectrums with The editor can be launched from the command line using the .specasm command. Specasm is an integrated editor/assembler. +> [!TIP] +> The ZX Spectrum Next version of Specasm allows the name of a single .x file to be passed on the command line. This file will be automatically loaded into the editor on startup, e.g., .specasm game.x. + The editor allows one single source, .x, file to be edited at any one time. Each such file can contain a maximum of 512 lines. Each line can be - An empty line @@ -60,11 +63,19 @@ On entering command mode, a '>' prompt appears at the bottom of the screen. Fro | g *line number* | Moves the cursor to the specified line number| | f *string* | Searches for the first instance of *string* starting from the current line. There's no wrap around. | +The Next version of Specasm uses one of the Next's 8Kb memory banks to implement a clipboard. This provides a more traditional copy, cut and paste mechanism than the copy and move commands described above, and allows code to be copied from one file to another. Clipboard support is provided via three new Next specific commands. + +| Command | Description | +|---------|-------------| +| x | Replaces the contents of the clipboard with the selected code and deletes the code from the current file | +| cc | Replaces the contents of the clipboard with the selected code | +| v | Pastes the contents of the clipboard into the selected code at the cursor position | + #### Selecting Mode -The *sel* command switches the editor into selection mode. In selection mode the user can press the up and down keys to select a block of code. They can also press the 'a' key to select the entire file. Only whole lines can be selected. To cancel the selection and return to editor mode, press SPACE. To delete the selected lines and return to editor mode, press DELETE. To confirm the selection and return to editor mode, press ENTER. Once the selection has been confirmed an '*' will appear in the status row at the bottom of the screen to the right of 'INS' or 'OVR'. This indicates that some lines are currently selected. These lines can be manipulated using the 'd', 'm', 'c' and 'b' commands described above. For example, to count the number of machine code bytes in the selected line, type SYMSHIFT+w followed by 'b' and ENTER. The editor is capable of computing the exact size in bytes of the machine code associated with the instructions and data in the selected region as it has already assembled these instructions and knows exactly how much space they will consume. +The *sel* command switches the editor into selection mode. In selection mode the user can press the up and down keys to select a block of code. They can also press the 'a' key to select the entire file. Only whole lines can be selected. To cancel the selection and return to editor mode, press SPACE. To delete the selected lines and return to editor mode, press DELETE. On the Next, the selected lines may be cut to the clipboard by typing 'x'. To confirm the selection and return to editor mode, press ENTER. Once the selection has been confirmed an '*' will appear in the status row at the bottom of the screen to the right of 'INS' or 'OVR'. This indicates that some lines are currently selected. These lines can be manipulated using the 'd', 'm', 'c','b', 'x' and 'cc' commands described above. For example, to count the number of machine code bytes in the selected line, type SYMSHIFT+w followed by 'b' and ENTER. The editor is capable of computing the exact size in bytes of the machine code associated with the instructions and data in the selected region as it has already assembled these instructions and knows exactly how much space they will consume. -All of the four commands that manipulate selections, cancel the selection once they have finished executing. So if you select a block of text, and then issue the 'b' command to count the number of bytes it consumes, you'll need to reselect the text once more to copy it. In addition, the current selection is cancelled if you make any changes to the contents of the editor, e.g., edit an existing line or insert a new one. +All of the six commands that manipulate selections, cancel the selection once they have finished executing. So if you select a block of text, and then issue the 'b' command to count the number of bytes it consumes, you'll need to reselect the text once more to copy it. In addition, the current selection is cancelled if you make any changes to the contents of the editor, e.g., edit an existing line or insert a new one. #### The Status Row @@ -578,7 +589,13 @@ The reverse process can be performed using the .saexport command. Note the use ## Program structure -A Specasm program is comprised of one or more .x files that occupy the same directory. When you build a Specasm program with the .salink command it looks for all the .x files in the folder in which it is run, and links them all together, concatenating them all into one single file and resolving any addresses, e.g., jump targets. One of the .x files in the current folder must contain a label called **Main**, .e.g, it must have the following statement somewhere within one of the files +A Specasm program is comprised of one or more .x files. When you build a Specasm program with the .salink command it looks for all the .x files in the folder in which it is run, and links them all together, concatenating them all into one single file and resolving any addresses, e.g., jump targets. + +> [!TIP] +> Specasm programs can actually span multiple directories. See the +, - and ! directives below. + + +One of the .x files must contain a label called **Main**, .e.g, it must have the following statement somewhere within one of the files ``` .Main @@ -586,7 +603,9 @@ A Specasm program is comprised of one or more .x files that occupy the same dire The name of the resulting binary will be derived from the name of the .x file that contains the **Main** label. So if a project places the Main label in a file called **game.x**, the name of the resulting binary created by salink will be **game**. -The salink command will place the code from the .x file that declares the Main label first in the newly created binary. The order in which the rest of the code is written to the binary is arbitrary and the user has no control over this. They can however, ask the linker to generate a map file to figure out where all the symbols ended up. This is done by specifying the **map** directive on one line of one .x file, e.g., +The salink command will place the code from the .x file that declares the Main label first in the newly created binary. In Specasm versions v7 and eearlier, the order in which the rest of the code is written to the binary is arbitrary and the user has no control over this. In Specasm v8 and above, the order in which the code from the remaining .x files is written to the target binary is defined by the alphabetical order (ascending) of their names. Suppose our program consisted of 3 files, main.x, 02_code.x, 01_data.x, the data/code in main.x would be written first, followed by the contents of 01_data.x, and finally, by the contents of 02_code.x. + +You can ask the linker to generate a map file to figure out where all the symbols ended up. This is done by specifying the **map** directive on one line of one .x file, e.g., ``` map @@ -621,9 +640,12 @@ org 23760 will cause the linked program to be assembled at 23760. -The linker doesn't currently create a loader program or a tap file. This can however be done using the samake command. +The linker only creates pure binary files. It isn't capable of creating a loader program or a tap file. This is the task of the samake program, introduced in Specasm v7. See below for more details. -### Libraries +Specasm projects are not limited to the .x files in the main directory. The '-' and '+' directives can be used to include .x files in other directories, allowing a Specasm project to span multiple directories. See below for more details. + + +### Multi-directory Projects and Libraries Specasm supports two directives that allow the user to include .x files from other directories in the final executable. @@ -634,7 +656,9 @@ Specasm supports two directives that allow the user to include .x files from oth These are both linker directives rather than assembler directives. The target of these directives are not included directly into the current source file. Instead they are added to the final binary when it is linked. The '-' directive is intended to be used to create custom libraries or to split your program into multiple folders. The '+' directive is intended to be used to include .x files from a future Specasm standard library. The trailing '.x' extension in is optional. -Here are some examples of their use +If the path passed to a '-' or a '+' directive is itself a directory, Specasm (v8 and above) will add all the .x files it can find in that directory, and only that directory, to the final binary. + +Here are some examples of '-' and '+' ``` ; include /specasm/gr/rows.x @@ -642,7 +666,60 @@ Here are some examples of their use ; include ./lib/math.x -lib/math.x + +; include all .x files in mylib +; Specasm v8 and above. +-mylib +``` + +The '-' and '+' directives will not add .x files located in any sub-directory of an included directory. For example, + +``` +-mylib +``` + +will add mylib/peqnp.x but it will not add mylib/tests/binpack.x. If mylib/tests/binpack.x is needed in the project it will need to be included separately, either with a + ``` +-mylib/tests/binpack.x +``` + +or + +``` +-mylib/tests +``` + +if all the .x files in mylib/tests are to be part of the project. + +### Binary files + +Specasm supports one directive that allows a file to be inserted directly into the final binary at the position at which the directive appears. This is useful when including binary data directly into your final executable rather than shipping it as a separate file and reading it in at runtime, or encoding it using db or dw statements, which isn't really practical when there's a lot of data. + +| Directive | Description | +|--------------|--------------------------------------------------------------------------------------------| +| ! | Filename is either an absolute or a relative path (relative to the current .x file) | + +For example, + +``` + ; load pointer to sprite data into hl + + ld hl, sprites + + ; Load size of sprite file into bc + + ld bc, =sprite_end-sprites + + ; do something with the sprites + + ret +.sprites +! spritefile +.sprite_end +``` + +The data in spritefile will be inserted between the .sprites and .sprite_end label in the final binary by the linker. The register bc will contain the size of that file. ## Creating Loaders with SAMAKE diff --git a/src/clipboard.c b/src/clipboard.c new file mode 100644 index 0000000..67f77af --- /dev/null +++ b/src/clipboard.c @@ -0,0 +1,114 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifdef SPECASM_TARGET_NEXT_OPCODES + +#include +#include + +#include "clipboard.h" +#include "line.h" + +#define SPECASM_CLIP_MAX_SIZE (15 * 512) + +static uint8_t clip_buffer[SPECASM_CLIP_MAX_SIZE]; + +static uint16_t clip_end_ptr; +static uint16_t clip_lines; + +#ifdef SPECASM_NEXT_BANKED +void specasm_clip_reset_banked(void) +#else +void specasm_clip_reset(void) +#endif +{ + clip_end_ptr = 0; + clip_lines = 0; +} + +#ifdef SPECASM_NEXT_BANKED +void specasm_clip_add_line_banked_e(const char *line) +#else +void specasm_clip_add_line_e(const char *line) +#endif +{ + uint8_t len; + const char *end_ptr; + + /* + * Skip leading and trailing white space. We don't need to store this. + */ + + end_ptr = &line[SPECASM_LINE_MAX_LEN - 1]; + while ((end_ptr >= line) && (*end_ptr == ' ')) + end_ptr--; + end_ptr++; + + if (line == end_ptr) { + clip_buffer[clip_end_ptr++] = 0; + } else { + while (*line == ' ') + line++; + + len = end_ptr - line; + if (len + clip_end_ptr >= SPECASM_CLIP_MAX_SIZE) { + err_type = SPECASM_ERROR_TOO_MANY_LINES; + return; + } + + clip_buffer[clip_end_ptr++] = len; + memcpy(&clip_buffer[clip_end_ptr], line, len); + clip_end_ptr += len; + } + clip_lines++; +} + +#ifdef SPECASM_NEXT_BANKED +uint16_t specasm_clip_get_line_banked(uint16_t ptr, char *buffer) +#else +uint16_t specasm_clip_get_line(uint16_t ptr, char *buffer) +#endif +{ + uint8_t len; + uint8_t spaces; + + if (ptr >= clip_end_ptr) + return 0; + + len = clip_buffer[ptr++]; + if (len > 0) { + memcpy(buffer, &clip_buffer[ptr], len); + ptr += len; + } + + spaces = SPECASM_LINE_MAX_LEN - len; + if (spaces > 0) + memset(&buffer[len], ' ', spaces); + buffer[len + spaces] = 0; + + return ptr; +} + +#ifdef SPECASM_NEXT_BANKED +uint16_t specasm_clip_get_line_count_banked(void) +#else +uint16_t specasm_clip_get_line_count(void) +#endif +{ + return clip_lines; +} + +#endif diff --git a/src/clipboard.h b/src/clipboard.h new file mode 100644 index 0000000..f779088 --- /dev/null +++ b/src/clipboard.h @@ -0,0 +1,27 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef CLIPBOARD_H +#define CLIPBOARD_H + +#include + +void specasm_clip_reset(void); +void specasm_clip_add_line_e(const char *line); +uint16_t specasm_clip_get_line(uint16_t ptr, char *buffer); +uint16_t specasm_clip_get_line_count(void); + +#endif diff --git a/src/editor.c b/src/editor.c index f0d8e0e..83c2ee3 100644 --- a/src/editor.c +++ b/src/editor.c @@ -20,6 +20,10 @@ #include "peer.h" #include "state.h" +#ifdef SPECASM_TARGET_NEXT_OPCODES +#include "clipboard.h" +#endif + #include #include @@ -116,11 +120,17 @@ static void specasm_dump_line_e(unsigned int l, uint8_t r, uint8_t inv) static uint8_t prv_selecting_delete(void); static void prv_selecting_count(void); -static void prv_selecting_copy(void); +static void prv_selecting_copy_e(void); static void prv_selecting_move(void); static void prv_goto(const char *num); static void prv_find(const char *needle); +#ifdef SPECASM_TARGET_NEXT_OPCODES +static void prv_selecting_clip_copy_e(void); +static uint8_t prv_selecting_clip_cut_e(void); +static void prv_selecting_clip_paste_e(void); +#endif + static void prv_num_to_char(char *buf, unsigned int num, uint8_t digits) { do { @@ -526,7 +536,9 @@ static uint8_t prv_single_char_command_e(uint8_t ch) prv_selecting_count(); break; case 'c': - prv_selecting_copy(); + prv_selecting_copy_e(); + if (err_type != SPECASM_ERROR_OK) + return 0; break; case 'm': prv_selecting_move(); @@ -542,6 +554,18 @@ static uint8_t prv_single_char_command_e(uint8_t ch) } specasm_save_e(current_fname); break; +#ifdef SPECASM_TARGET_NEXT_OPCODES + case 'x': + (void)prv_selecting_clip_cut_e(); + if (err_type != SPECASM_ERROR_OK) + return 0; + break; + case 'v': + prv_selecting_clip_paste_e(); + if (err_type != SPECASM_ERROR_OK) + return 0; + break; +#endif default: err_type = SPECASM_ERROR_BAD_COMMAND; return 0; @@ -596,6 +620,7 @@ static char *prv_complete_filename_e(char *com, uint8_t len) static uint8_t prv_long_command_e(char *com, uint8_t len) { + specasm_error_t old_err; uint8_t reset = 0; if (!strcmp(com, "sel")) { @@ -617,12 +642,22 @@ static uint8_t prv_long_command_e(char *com, uint8_t len) reset = 1; current_fname[0] = 0; } else { + old_err = err_type; + err_type = SPECASM_ERROR_OK; + if (old_err == SPECASM_ERROR_OK) + strcpy(current_fname, com); line = row = col = select_end = select_start = 0; specasm_cls(SPECASM_CODE_COLOUR | SPECASM_LABEL_BACKGROUND); prv_draw_screen(0); - if (err_type == SPECASM_ERROR_OK) - strcpy(current_fname, com); + if (old_err != SPECASM_ERROR_OK) { + specasm_text_set_flash(command_col, + SPECASM_MAX_ROWS, 0); + err_type = old_err; + prv_draw_error(); + err_type = SPECASM_ERROR_OK; + specasm_sleep_ms(1000); + } } } else if (com[0] == 's' && com[1] == ' ') { com = prv_complete_filename_e(com, len); @@ -635,6 +670,12 @@ static uint8_t prv_long_command_e(char *com, uint8_t len) prv_goto(&com[2]); } else if (com[0] == 'f' && com[1] == ' ') { prv_find(&com[2]); +#ifdef SPECASM_TARGET_NEXT_OPCODES + } else if ((com[0] == 'c') && (com[1] == 'c')) { + prv_selecting_clip_copy_e(); + if (err_type != SPECASM_ERROR_OK) + return 0; +#endif } else { err_type = SPECASM_ERROR_BAD_COMMAND; return 0; @@ -738,12 +779,12 @@ static uint8_t prv_check_delete_line(void) return 1; } -static void prv_goto_line(unsigned int target) +static uint8_t prv_goto_line(unsigned int target) { unsigned int starting_line; if (target == line) - return; + return 0; starting_line = line - row; line = target; @@ -755,7 +796,7 @@ static void prv_goto_line(unsigned int target) if ((line >= starting_line) && (line < starting_line + SPECASM_MAX_ROWS)) { row = line - starting_line; - return; + return 0; } if (line + SPECASM_MAX_ROWS <= state.lines.num_lines) { @@ -766,6 +807,8 @@ static void prv_goto_line(unsigned int target) row = line - starting_line; } prv_draw_screen(starting_line); + + return 1; } static void prv_goto(const char *num) @@ -786,7 +829,7 @@ static void prv_goto(const char *num) target = state.lines.num_lines - 1; } - prv_goto_line(target); + (void)prv_goto_line(target); } static void prv_find(const char *needle) @@ -798,7 +841,7 @@ static void prv_find(const char *needle) if (err_type != SPECASM_ERROR_OK) return; if (strstr(scratch, needle)) { - prv_goto_line(l); + (void)prv_goto_line(l); return; } } @@ -1167,6 +1210,123 @@ static uint8_t prv_selecting_delete(void) return 1; } +static uint16_t prv_make_space_e(uint16_t line_count) +{ + uint16_t size; + uint16_t lines_left; + + size = line_count; + if (ovr) { + lines_left = state.lines.num_lines - line; + if (lines_left > size) + lines_left = size; + size -= lines_left; + } + + if (size > 0) { + specasm_insert_lines_e(line, size); + if (err_type != SPECASM_ERROR_OK) + return 0; + } + + return size; +} + +#ifdef SPECASM_TARGET_NEXT_OPCODES + +static void prv_clip_copy_e(const char *command) +{ + unsigned int i; + + specasm_clip_reset(); + for (i = select_start; i < select_end; i++) { + specasm_format_line_e(line_buf, i); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + specasm_clip_add_line_e(line_buf); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + } + + return; + +on_error: + + /* + * We need to put line_buf back the way it was. + */ + + memset(line_buf, ' ', SPECASM_LINE_MAX_LEN); + line_buf[0] = '>'; + line_buf[2] = command[0]; + if (command[1]) + line_buf[3] = command[1]; +} + +static void prv_selecting_clip_copy_e(void) +{ + if (select_start >= select_end) + return; + prv_clip_copy_e("cc"); +} + +static uint8_t prv_selecting_clip_cut_e(void) +{ + if (select_start >= select_end) + return 0; + prv_clip_copy_e("x"); + if (err_type != SPECASM_ERROR_OK) + return 0; + + if (!prv_selecting_delete()) + return 0; + + prv_draw_screen(line - row); + specasm_text_set_flash(col, row, SPECASM_FLASH); + specasm_draw_status(); + + return 1; +} + +static void prv_selecting_clip_paste_e(void) +{ + uint16_t i; + uint16_t ptr = 0; + uint16_t line_count = specasm_clip_get_line_count(); + + if (line_count == 0) + return; + + (void)prv_make_space_e(line_count); + if (err_type != SPECASM_ERROR_OK) + return; + + i = line; + ptr = specasm_clip_get_line(ptr, line_buf); + while (ptr) { + specasm_parse_line_e(i, line_buf); + + /* + * This can't really error unless there's some memory + * corruption. If we return an error here we'll need + * to reset line_buf. As this cannot happen it's not + * worth the code to do it. We'll clear the error here + * to prevent the corrupted line_buf from being used + * when the command prompt is redisplayed. + */ + + err_type = SPECASM_ERROR_OK; + ptr = specasm_clip_get_line(ptr, line_buf); + i++; + } + prv_draw_screen(line - row); + if (select_start < select_end) { + select_end = select_start = 0; + specasm_draw_status(); + } +} +#endif + static void prv_selecting_count(void) { uint16_t i; @@ -1194,32 +1354,21 @@ static void prv_selecting_count(void) select_start = select_end = 0; } -static void prv_selecting_copy(void) +static void prv_selecting_copy_e(void) { uint16_t i; uint16_t j; - uint16_t size; - uint16_t lines_left; uint16_t src_index; + uint16_t size; specasm_line_t *src; specasm_line_t *dst; if (select_start >= select_end) return; - size = select_end - select_start; - if (ovr) { - lines_left = state.lines.num_lines - line; - if (lines_left > size) - lines_left = size; - size -= lines_left; - } - - if (size > 0) { - specasm_insert_lines_e(line, size); - if (err_type != SPECASM_ERROR_OK) - return; - } + size = prv_make_space_e(select_end - select_start); + if (err_type != SPECASM_ERROR_OK) + return; j = line; for (i = select_start; i < select_end; i++) { @@ -1302,7 +1451,21 @@ static void prv_selecting_keypress(uint8_t k) switch (k) { case SPECASM_KEY_DELETE: +#ifdef SPECASM_TARGET_NEXT_OPCODES + case 'x': + if (k == SPECASM_KEY_DELETE) { + update = prv_selecting_delete(); + } else { + update = prv_selecting_clip_cut_e(); + if (err_type != SPECASM_ERROR_OK) { + prv_draw_error(); + specasm_sleep_ms(1000); + update = 1; + } + } +#else update = prv_selecting_delete(); +#endif case ' ': select_start = 0; select_end = 0; @@ -1374,6 +1537,47 @@ void specasm_handle_key_press(uint8_t k) prv_keypress(k); } +#ifdef SPECASM_TARGET_NEXT +void specasm_editor_preload(const char *fname) +{ + char *completed_fname; + size_t len = strlen(fname); + + if (len > MAX_FNAME) { + err_type = SPECASM_ERROR_BAD_FNAME; + goto on_error; + } + + /* + * We re-use prv_complete_filename_e here to avoid having to + * duplicate the code. This function expects to be passed a + * buffer (which in effect is a pointer to the start of the + * line_buf buffer), the first character of which is 'l' which it skips. + * So we'll just duplicate this setup so we can re-use the code. + */ + + line_buf[0] = ' '; + strcpy(&line_buf[1], fname); + completed_fname = prv_complete_filename_e(line_buf, len); + + specasm_load_e(completed_fname); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + strcpy(current_fname, completed_fname); + line = row = col = select_end = select_start = 0; + prv_draw_screen(0); + + return; + +on_error: + prv_draw_error(); + specasm_sleep_ms(1000); + err_type = SPECASM_ERROR_OK; + specasm_editor_reset(); +} +#endif + void specasm_editor_reset(void) { specasm_line_t *l; diff --git a/src/editor.h b/src/editor.h index 5b7483c..5f7f536 100644 --- a/src/editor.h +++ b/src/editor.h @@ -44,6 +44,13 @@ extern unsigned int line; extern uint8_t col; extern uint8_t quitting; +#ifdef SPECASM_TARGET_NEXT +/* + * Used to preload a .x file to edit on startup. Assumes that the screen has + * already been cleared. + */ +void specasm_editor_preload(const char *fname); +#endif void specasm_draw_status(void); void specasm_handle_key_press(uint8_t k); void specasm_editor_reset(void); diff --git a/src/editor_test_content.c b/src/editor_test_content.c index d3432a5..6f36b6a 100644 --- a/src/editor_test_content.c +++ b/src/editor_test_content.c @@ -65,10 +65,19 @@ const char *const formatted_test_data = FORMATTED_TEST_DATA " "; + const char *const formatted_test_data2x = FORMATTED_TEST_DATA FORMATTED_TEST_DATA " "; +const char *const formatted_clip_oom = FORMATTED_TEST_DATA + " " + " " + " " + " " + " " + " "; + const char *const formatted_last_deleted = " " ".this_is_the_main_loop " @@ -947,7 +956,8 @@ const editor_test_t editor_tests[] = { EDITOR_KEY_COMMAND "c" EDITOR_KEY_ENTER, &formatted_test_data2x[4 * SPECASM_LINE_MAX_LEN], "", - { .line = 26, .row = 22, .command_col = 3, .select_end = 432 }, + { .mode = 2,.line = 26, .row = 22, .command_col = 3, + .select_end = 432 }, 432, }, { @@ -1221,6 +1231,359 @@ const editor_test_t editor_tests[] = { .select_end = 2 }, 11, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "simple_cut", + "ld a, 10" EDITOR_KEY_ENTER + "db 10" EDITOR_KEY_ENTER + "bit 3, (ix + 127)" EDITOR_KEY_ENTER + "@hello" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP + EDITOR_KEY_COMMAND "sel" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "x" EDITOR_KEY_ENTER, + " bit 3, (ix+127) " + "@hello " + EDITOR_BLANK_LINE, + "", + { .row = 0, .line = 0, .command_col = 3 }, + 3, + }, + { + "cut_and_paste_in_place", + test_data EDITOR_KEY_UP EDITOR_KEY_UP + EDITOR_KEY_COMMAND "sel" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP EDITOR_KEY_UP EDITOR_KEY_UP + EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "x" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + &formatted_test_data[0], + "", + { .line = 20, .command_col = 3, .row = 20 }, + 27, + }, + { + "cut_and_paste_below", + ";1" EDITOR_KEY_ENTER + ";2" EDITOR_KEY_ENTER + ";3" EDITOR_KEY_ENTER + ";4" EDITOR_KEY_ENTER + ";5" EDITOR_KEY_ENTER + ";6" EDITOR_KEY_ENTER + ";7" EDITOR_KEY_ENTER + ";8" EDITOR_KEY_ENTER + ";9" EDITOR_KEY_ENTER + ";10" EDITOR_KEY_ENTER + ";11" EDITOR_KEY_ENTER + ";12" EDITOR_KEY_ENTER + ";13" EDITOR_KEY_ENTER + ";14" EDITOR_KEY_ENTER + EDITOR_BUF_START EDITOR_KEY_DOWN + EDITOR_KEY_COMMAND"sel" EDITOR_KEY_ENTER + EDITOR_KEY_DOWN EDITOR_KEY_DOWN EDITOR_KEY_DOWN + EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "x" EDITOR_KEY_ENTER + EDITOR_BUF_END + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + ";1 " + ";5 " + ";6 " + ";7 " + ";8 " + ";9 " + ";10 " + ";11 " + ";12 " + ";13 " + ";14 " + ";2 " + ";3 " + ";4 " + EDITOR_BLANK_LINE, + "", + { .line = 11, .command_col = 3, .row = 11 }, + 15, + }, + { + "cut_and_paste_above", + ";1" EDITOR_KEY_ENTER + ";2" EDITOR_KEY_ENTER + ";3" EDITOR_KEY_ENTER + ";4" EDITOR_KEY_ENTER + ";5" EDITOR_KEY_ENTER + ";6" EDITOR_KEY_ENTER + ";7" EDITOR_KEY_ENTER + ";8" EDITOR_KEY_ENTER + ";9" EDITOR_KEY_ENTER + ";10" EDITOR_KEY_ENTER + ";11" EDITOR_KEY_ENTER + ";12" EDITOR_KEY_ENTER + ";13" EDITOR_KEY_ENTER + ";14" EDITOR_KEY_ENTER + EDITOR_BUF_END EDITOR_KEY_UP + EDITOR_KEY_COMMAND"sel" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP EDITOR_KEY_UP + EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "x" EDITOR_KEY_ENTER + EDITOR_BUF_START EDITOR_KEY_DOWN + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + ";1 " + ";11 " + ";12 " + ";13 " + ";2 " + ";3 " + ";4 " + ";5 " + ";6 " + ";7 " + ";8 " + ";9 " + ";10 " + ";14 " + EDITOR_BLANK_LINE, + "", + { .line = 1, .command_col = 3, .row = 1 }, + 15, + }, + { + "cut_all_test_data", + test_data + EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "x" EDITOR_KEY_ENTER, + EDITOR_BLANK_LINE, + "", + { .line = 0, .row = 0 }, + 1, + }, + { + "cut_and_paste_test_data", + test_data + EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "x" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + &formatted_test_data[0], + "", + { .line = 0, .command_col = 3, .row = 0 }, + 28, + }, + { + "copy_clip_into_selected_area", + "ld a, 10" EDITOR_KEY_ENTER + "db 10" EDITOR_KEY_ENTER + "bit 3, (ix + 127)" EDITOR_KEY_ENTER + "@hello" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP + EDITOR_KEY_COMMAND "sel" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP EDITOR_KEY_ENTER + EDITOR_KEY_DOWN + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + " ld a, 10 " + " ld a, 10 " + "db 10 " + "db 10 " + " bit 3, (ix+127) " + "@hello " + EDITOR_BLANK_LINE, + "", + { .row = 1, .line = 1, .command_col = 3 }, + 7, + }, + { + "copy_clip_above_selected_area", + "ld a, 10" EDITOR_KEY_ENTER + "db 10" EDITOR_KEY_ENTER + "bit 3, (ix + 127)" EDITOR_KEY_ENTER + "@hello" EDITOR_KEY_ENTER + EDITOR_KEY_UP + EDITOR_KEY_COMMAND "sel" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + "db 10 " + " bit 3, (ix+127) " + " ld a, 10 " + "db 10 " + " bit 3, (ix+127) " + "@hello " + EDITOR_BLANK_LINE, + "", + { .row = 0, .line = 0, .command_col = 3 }, + 7, + }, + { + "copy_clip_below_selected_area", + "ld a, 10" EDITOR_KEY_ENTER + "db 10" EDITOR_KEY_ENTER + "bit 3, (ix + 127)" EDITOR_KEY_ENTER + "@hello" EDITOR_KEY_ENTER + EDITOR_BUF_START + EDITOR_KEY_COMMAND "sel" EDITOR_KEY_ENTER + EDITOR_KEY_DOWN EDITOR_KEY_DOWN EDITOR_KEY_ENTER + EDITOR_BUF_END + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + " ld a, 10 " + "db 10 " + " bit 3, (ix+127) " + "@hello " + " ld a, 10 " + "db 10 " + EDITOR_BLANK_LINE, + "", + { .row = 4, .line = 4, .command_col = 3 }, + 7, + }, + { + "copy_clip_whole_buffer", + test_data EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + &formatted_test_data2x[4 * SPECASM_LINE_MAX_LEN], + "", + { .line = 26, .row = 22, .command_col = 3 }, + 54, + }, + { + "copy_clip_oom", + test_data EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "a" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + &formatted_test_data2x[4 * SPECASM_LINE_MAX_LEN], + "", + { .mode = 2, .line = 26, .row = 22, .command_col = 3, + .select_end = 432 }, + 432, + }, + { + "paste_clip_empty", + test_data + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + &formatted_test_data[4 * SPECASM_LINE_MAX_LEN], + "", + { .line = 26, .row = 22, .command_col = 3 }, + 27, + }, + { + "copy_clip_empty", + test_data + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER, + &formatted_test_data[4 * SPECASM_LINE_MAX_LEN], + "", + { .line = 26, .row = 22, .command_col = 4 }, + 27, + }, + { + "cut_clip_empty", + test_data + EDITOR_KEY_COMMAND "x" EDITOR_KEY_ENTER, + &formatted_test_data[4 * SPECASM_LINE_MAX_LEN], + "", + { .line = 26, .row = 22, .command_col = 3 }, + 27, + }, + { + "paste_clip_overwrite_1", + ";1" EDITOR_KEY_ENTER + ";2" EDITOR_KEY_ENTER + ";3" EDITOR_KEY_ENTER + ";4" EDITOR_KEY_ENTER + ";5" EDITOR_KEY_ENTER + ";6" EDITOR_KEY_ENTER + ";7" EDITOR_KEY_ENTER + ";8" EDITOR_KEY_ENTER + ";9" EDITOR_KEY_ENTER + ";10" EDITOR_KEY_ENTER + ";11" EDITOR_KEY_ENTER + ";12" EDITOR_KEY_ENTER + ";13" EDITOR_KEY_ENTER + ";14" EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "sel" EDITOR_KEY_ENTER + EDITOR_KEY_UP EDITOR_KEY_UP EDITOR_KEY_UP + EDITOR_KEY_UP EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_INSERT + EDITOR_BUF_START EDITOR_KEY_DOWN + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + ";1 " + ";11 " + ";12 " + ";13 " + ";14 " + ";6 " + ";7 " + ";8 " + ";9 " + ";10 " + ";11 " + ";12 " + ";13 " + ";14 " + EDITOR_BLANK_LINE, + "", + { .line = 1, .command_col = 3, .row = 1, .ovr = 1 }, + 15, + }, + { + "paste_clip_overwrite_2", + ";1" EDITOR_KEY_ENTER + ";2" EDITOR_KEY_ENTER + ";3" EDITOR_KEY_ENTER + ";4" EDITOR_KEY_ENTER + ";5" EDITOR_KEY_ENTER + ";6" EDITOR_KEY_ENTER + ";7" EDITOR_KEY_ENTER + ";8" EDITOR_KEY_ENTER + ";9" EDITOR_KEY_ENTER + ";10" EDITOR_KEY_ENTER + ";11" EDITOR_KEY_ENTER + ";12" EDITOR_KEY_ENTER + ";13" EDITOR_KEY_ENTER + ";14" EDITOR_KEY_ENTER + EDITOR_BUF_START + EDITOR_KEY_COMMAND "sel" EDITOR_KEY_ENTER + EDITOR_KEY_DOWN EDITOR_KEY_DOWN EDITOR_KEY_DOWN + EDITOR_KEY_DOWN EDITOR_KEY_ENTER + EDITOR_KEY_COMMAND "cc" EDITOR_KEY_ENTER + EDITOR_KEY_INSERT + EDITOR_BUF_END EDITOR_KEY_UP + EDITOR_KEY_COMMAND "v" EDITOR_KEY_ENTER, + ";1 " + ";2 " + ";3 " + ";4 " + ";5 " + ";6 " + ";7 " + ";8 " + ";9 " + ";10 " + ";11 " + ";12 " + ";13 " + ";1 " + ";2 " + ";3 " + ";4 ", + "", + { .line = 13, .command_col = 3, .row = 13, .ovr = 1 }, + 17, + }, +#endif }; const size_t editor_tests_count = sizeof(editor_tests) / sizeof(editor_test_t); diff --git a/src/editor_tests.c b/src/editor_tests.c index 49a86c7..5db1656 100644 --- a/src/editor_tests.c +++ b/src/editor_tests.c @@ -17,6 +17,10 @@ #include #include +#ifdef SPECASM_TARGET_NEXT_OPCODES +#include "clipboard.h" +#endif + #include "editor.h" #include "editor_test_content.h" #include "state.h" @@ -88,6 +92,9 @@ static int prv_check_state(const editor_test_t *t) static int prv_run_test(const editor_test_t *t) { specasm_editor_reset(); +#ifdef SPECASM_TARGET_NEXT_OPCODES + specasm_clip_reset(); +#endif printf("%s: ", t->name); for (size_t i = 0; i < strlen(t->input); i++) { diff --git a/src/expression.c b/src/expression.c index cafaa65..f80263a 100644 --- a/src/expression.c +++ b/src/expression.c @@ -94,15 +94,17 @@ struct salink_token_t_ { typedef struct salink_token_t_ salink_token_t; -static const char *prv_exp_priority4_e(const char *str, salink_obj_t *obj, - int16_t *e, uint8_t depth, - uint8_t is_global, uint16_t line_no); +static salink_obj_t *g_obj; -static void prv_equ_eval_local_e(salink_obj_t *obj, salink_label_t *label, - uint8_t depth, uint16_t line_no); +static const char *prv_exp_priority4_e(const char *str, int16_t *e, + uint8_t depth, uint8_t is_global, + uint16_t line_no); -const char *prv_get_token_e(const char *buf, salink_obj_t *obj, - salink_token_t *tok, uint8_t is_global) +static void prv_equ_eval_local_e(salink_label_t *label, uint8_t depth, + uint16_t line_no); + +const char *prv_get_token_e(const char *buf, salink_token_t *tok, + uint8_t is_global) { const char *start; char *end_ptr; @@ -183,11 +185,12 @@ const char *prv_get_token_e(const char *buf, salink_obj_t *obj, c = *start; if ((c >= 'A') && (c <= 'Z')) { - tok->data.id = salink_find_global_label_e(scratch, obj); + tok->data.id = + salink_find_global_label_e(scratch, g_obj); tok->type = SALINK_TOKEN_GLOBAL_LABEL; } else { tok->data.id = - salink_find_local_label_e(scratch, len, obj); + salink_find_local_label_e(scratch, len, g_obj); if ((err_type == SPECASM_ERROR_ASSERT_BAD_STRING_ID) && (is_global)) { err_type = SALINK_ERROR_LOCAL_IN_GLOBAL_EQU; @@ -203,23 +206,22 @@ const char *prv_get_token_e(const char *buf, salink_obj_t *obj, return NULL; } -static const char *prv_exp_priority0_e(const char *str, salink_obj_t *obj, - int16_t *e, uint8_t depth, - uint8_t is_global, uint16_t line_no) +static const char *prv_exp_priority0_e(const char *str, int16_t *e, + uint8_t depth, uint8_t is_global, + uint16_t line_no) { salink_token_t tok; salink_label_t *label; salink_global_t *global; - str = prv_get_token_e(str, obj, &tok, is_global); + str = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) { return NULL; } switch (tok.type) { case SALINK_TOKEN_OP: - str = - prv_exp_priority4_e(str, obj, e, depth, is_global, line_no); + str = prv_exp_priority4_e(str, e, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; switch (tok.data.id) { @@ -230,7 +232,7 @@ static const char *prv_exp_priority0_e(const char *str, salink_obj_t *obj, *e = ~*e; break; case '(': - str = prv_get_token_e(str, obj, &tok, is_global); + str = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; if ((tok.type == SALINK_TOKEN_OP) && @@ -266,7 +268,7 @@ static const char *prv_exp_priority0_e(const char *str, salink_obj_t *obj, err_type = SALINK_ERROR_RECURISVE_EQU; return NULL; } - prv_equ_eval_local_e(obj, label, depth + 1, line_no); + prv_equ_eval_local_e(label, depth + 1, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; *e = label->data.off; @@ -292,7 +294,8 @@ static const char *prv_exp_priority0_e(const char *str, salink_obj_t *obj, err_type = SALINK_ERROR_RECURISVE_EQU; return NULL; } - salink_equ_eval_global_e(obj, global, label, depth + 1); + salink_equ_eval_global_e(g_obj, global, label, + depth + 1); if (err_type != SPECASM_ERROR_OK) return NULL; *e = label->data.off; @@ -312,9 +315,9 @@ static const char *prv_exp_priority0_e(const char *str, salink_obj_t *obj, return str; } -static const char *prv_exp_priority1_e(const char *str, salink_obj_t *obj, - int16_t *e, uint8_t depth, - uint8_t is_global, uint16_t line_no) +static const char *prv_exp_priority1_e(const char *str, int16_t *e, + uint8_t depth, uint8_t is_global, + uint16_t line_no) { salink_token_t tok; int16_t e1; @@ -322,11 +325,11 @@ static const char *prv_exp_priority1_e(const char *str, salink_obj_t *obj, char op; const char *next; - str = prv_exp_priority0_e(str, obj, &e1, depth, is_global, line_no); + str = prv_exp_priority0_e(str, &e1, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -335,8 +338,7 @@ static const char *prv_exp_priority1_e(const char *str, salink_obj_t *obj, if (op != '*' && op != '/' && op != '%') break; - str = prv_exp_priority0_e(next, obj, &e2, depth, is_global, - line_no); + str = prv_exp_priority0_e(next, &e2, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -356,7 +358,7 @@ static const char *prv_exp_priority1_e(const char *str, salink_obj_t *obj, e1 = e1 % e2; break; } - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; } @@ -366,9 +368,9 @@ static const char *prv_exp_priority1_e(const char *str, salink_obj_t *obj, return str; } -static const char *prv_exp_priority2_e(const char *str, salink_obj_t *obj, - int16_t *e, uint8_t depth, - uint8_t is_global, uint16_t line_no) +static const char *prv_exp_priority2_e(const char *str, int16_t *e, + uint8_t depth, uint8_t is_global, + uint16_t line_no) { salink_token_t tok; int16_t e1; @@ -376,11 +378,11 @@ static const char *prv_exp_priority2_e(const char *str, salink_obj_t *obj, char op; const char *next; - str = prv_exp_priority1_e(str, obj, &e1, depth, is_global, line_no); + str = prv_exp_priority1_e(str, &e1, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -389,8 +391,7 @@ static const char *prv_exp_priority2_e(const char *str, salink_obj_t *obj, if (op != '+' && op != '-') break; - str = prv_exp_priority1_e(next, obj, &e2, depth, is_global, - line_no); + str = prv_exp_priority1_e(next, &e2, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -402,7 +403,7 @@ static const char *prv_exp_priority2_e(const char *str, salink_obj_t *obj, e1 = e1 - e2; break; } - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; } @@ -412,9 +413,9 @@ static const char *prv_exp_priority2_e(const char *str, salink_obj_t *obj, return str; } -static const char *prv_exp_priority3_e(const char *str, salink_obj_t *obj, - int16_t *e, uint8_t depth, - uint8_t is_global, uint16_t line_no) +static const char *prv_exp_priority3_e(const char *str, int16_t *e, + uint8_t depth, uint8_t is_global, + uint16_t line_no) { salink_token_t tok; int16_t e1; @@ -422,11 +423,11 @@ static const char *prv_exp_priority3_e(const char *str, salink_obj_t *obj, char op; const char *next; - str = prv_exp_priority2_e(str, obj, &e1, depth, is_global, line_no); + str = prv_exp_priority2_e(str, &e1, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -436,8 +437,7 @@ static const char *prv_exp_priority3_e(const char *str, salink_obj_t *obj, (op != SALINK_TOKEN_ASR_VAL)) break; - str = prv_exp_priority2_e(next, obj, &e2, depth, is_global, - line_no); + str = prv_exp_priority2_e(next, &e2, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -449,7 +449,7 @@ static const char *prv_exp_priority3_e(const char *str, salink_obj_t *obj, e1 = e1 >> e2; break; } - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; } @@ -459,9 +459,9 @@ static const char *prv_exp_priority3_e(const char *str, salink_obj_t *obj, return str; } -static const char *prv_exp_priority4_e(const char *str, salink_obj_t *obj, - int16_t *e, uint8_t depth, - uint8_t is_global, uint16_t line_no) +static const char *prv_exp_priority4_e(const char *str, int16_t *e, + uint8_t depth, uint8_t is_global, + uint16_t line_no) { salink_token_t tok; int16_t e1; @@ -469,11 +469,11 @@ static const char *prv_exp_priority4_e(const char *str, salink_obj_t *obj, char op; const char *next; - str = prv_exp_priority3_e(str, obj, &e1, depth, is_global, line_no); + str = prv_exp_priority3_e(str, &e1, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -482,8 +482,7 @@ static const char *prv_exp_priority4_e(const char *str, salink_obj_t *obj, if (op != '&' && op != '|' && op != '^') break; - str = prv_exp_priority3_e(next, obj, &e2, depth, is_global, - line_no); + str = prv_exp_priority3_e(next, &e2, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return NULL; @@ -498,7 +497,7 @@ static const char *prv_exp_priority4_e(const char *str, salink_obj_t *obj, e1 = e1 ^ e2; break; } - next = prv_get_token_e(str, obj, &tok, is_global); + next = prv_get_token_e(str, &tok, is_global); if (err_type != SPECASM_ERROR_OK) return NULL; } @@ -508,19 +507,18 @@ static const char *prv_exp_priority4_e(const char *str, salink_obj_t *obj, return str; } -static int16_t prv_equ_eval_e(salink_obj_t *obj, const char *name, - uint8_t depth, uint8_t is_global, - uint16_t line_no) +static int16_t prv_equ_eval_e(const char *name, uint8_t depth, + uint8_t is_global, uint16_t line_no) { const char *str; int16_t e; salink_token_t tok; - str = prv_exp_priority4_e(name, obj, &e, depth, is_global, line_no); + str = prv_exp_priority4_e(name, &e, depth, is_global, line_no); if (err_type != SPECASM_ERROR_OK) return 0; - (void)prv_get_token_e(str, obj, &tok, is_global); + (void)prv_get_token_e(str, &tok, is_global); if (tok.type != SALINK_TOKEN_EOF) err_type = SALINK_ERROR_BAD_EXPRESSION; @@ -545,8 +543,8 @@ static int16_t prv_equ_eval_e(salink_obj_t *obj, const char *name, * evaluated. */ -static void prv_check_exp_err(salink_obj_t *obj, const char *name, - uint16_t line_no, uint8_t exact_line) +static void prv_check_exp_err(const char *name, uint16_t line_no, + uint8_t exact_line) { const char *err_msg; const char *local_msg; @@ -572,34 +570,30 @@ static void prv_check_exp_err(salink_obj_t *obj, const char *name, return; } snprintf(error_buf, sizeof(error_buf), "%s '%s' %s %s:%d", err_msg, - name, local_msg, obj->fname, line_no); + name, local_msg, g_obj->fname, line_no); } -static void prv_check_equ_err(salink_obj_t *obj, const char *name, - const char *equ, uint16_t line_no, - uint8_t exact_line) +static void prv_check_equ_err(const char *name, const char *equ, + uint16_t line_no, uint8_t exact_line) { snprintf(scratch, sizeof(scratch), "%s = %s", name, equ); - prv_check_exp_err(obj, scratch, line_no, exact_line); + prv_check_exp_err(scratch, line_no, exact_line); } -#ifdef SPECASM_NEXT_BANKED -int16_t salink_equ_eval_banked_e(salink_obj_t *obj, const char *str, - uint16_t line_no) -#else -int16_t salink_equ_eval_e(salink_obj_t *obj, const char *str, uint16_t line_no) -#endif +static int16_t prv_equ_eval_wrapper_e(salink_obj_t *obj, const char *str, + uint16_t line_no) { int16_t e; - e = prv_equ_eval_e(obj, str, 0, 0, line_no); - prv_check_exp_err(obj, str, line_no, 1); + g_obj = obj; + e = prv_equ_eval_e(str, 0, 0, line_no); + prv_check_exp_err(str, line_no, 1); return e; } -static void prv_equ_eval_local_e(salink_obj_t *obj, salink_label_t *label, - uint8_t depth, uint16_t line_no) +static void prv_equ_eval_local_e(salink_label_t *label, uint8_t depth, + uint16_t line_no) { const char *name; uint8_t id; @@ -610,10 +604,10 @@ static void prv_equ_eval_local_e(salink_obj_t *obj, salink_label_t *label, if (err_type != SPECASM_ERROR_OK) return; - label->data.off = prv_equ_eval_e(obj, name, depth, 0, line_no); + label->data.off = prv_equ_eval_e(name, depth, 0, line_no); if (err_type >= SPECASM_MAX_ERRORS) { str = salink_get_label_str_e(label->id, label->type); - prv_check_equ_err(obj, str, name, line_no, 0); + prv_check_equ_err(str, name, line_no, 0); return; } @@ -633,11 +627,235 @@ void salink_equ_eval_global_e(salink_obj_t *obj, salink_global_t *global, { const char *name = global->name + strlen(global->name) + 1; - label->data.off = prv_equ_eval_e(obj, name, depth, 1, global->line_no); + g_obj = obj; + label->data.off = prv_equ_eval_e(name, depth, 1, global->line_no); if (err_type >= SPECASM_MAX_ERRORS) { - prv_check_equ_err(obj, global->name, name, global->line_no, 1); + prv_check_equ_err(global->name, name, global->line_no, 1); return; } label->type = SALINK_LABEL_TYPE_EQU_EVAL_GLOBAL; } + +static int16_t prv_eval_exp_from_id_e(salink_obj_t *obj, unsigned int line_no, + uint8_t id, uint8_t lng) +{ + const char *str; + int16_t exp; + + str = salink_get_label_str_e(id, lng); + if (err_type != SPECASM_ERROR_OK) + return 0; + + exp = prv_equ_eval_wrapper_e(obj, str, line_no); + + if ((err_type != SPECASM_ERROR_OK) && (err_type < SPECASM_MAX_ERRORS)) { + snprintf(error_buf, sizeof(error_buf), "%s:%d %s", obj->fname, + line_no, specasm_error_msg(err_type)); + err_type = SALINK_ERROR_BAD_EXP; + } + + return exp; +} + +static int16_t prv_eval_equ_label(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no, uint8_t id) +{ + uint8_t label_type; + uint8_t lng; + + label_type = specasm_line_get_addr_type(line); + lng = label_type == SPECASM_FLAGS_ADDR_SHORT ? 0 : 1; + return prv_eval_exp_from_id_e(obj, line_no, id, lng); +} + +static void prv_eval_equ_8bit_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no, uint8_t loc) +{ + int16_t exp; + + exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); + if (err_type != SPECASM_ERROR_OK) + return; + + if ((exp > 255) || (exp < -128)) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d immediate too big :%d", obj->fname, line_no, + exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + line->data.op_code[loc] = exp; +} + +static void prv_eval_equ_16bit_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no, uint8_t loc) +{ + int16_t exp; + + exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); + if (err_type != SPECASM_ERROR_OK) + return; + + *((uint16_t *)&line->data.op_code[loc]) = exp; +} + +#ifdef SPECASM_TARGET_NEXT_OPCODES +static void prv_eval_equ_push_imm_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no) +{ + int16_t exp; + uint8_t exp_rev[2]; + + /* + * The push imm instruction is weird in that the immediate is encoded + * in big endian format. If we're dealing with an expression this means + * that the expression id will be in byte 3 and not byte 2 as one might + * expect. + * + * We also need to make sure we reverse the bytes of the evaluated + * expression. + */ + + exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[3]); + if (err_type != SPECASM_ERROR_OK) + return; + memcpy(exp_rev, &exp, 2); + line->data.op_code[2] = exp_rev[1]; + line->data.op_code[3] = exp_rev[0]; +} +#endif + +#ifdef SPECASM_NEXT_BANKED +void salink_apply_expressions_banked_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no) +#else +void salink_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no) +#endif +{ + int16_t exp; + uint8_t opcode0; + + line->type -= SPECASM_LINE_TYPE_EXP_ADJ; + + switch (line->type) { + case SPECASM_LINE_TYPE_ADC: + case SPECASM_LINE_TYPE_ADD: + case SPECASM_LINE_TYPE_AND: + case SPECASM_LINE_TYPE_CP: + case SPECASM_LINE_TYPE_IN: + case SPECASM_LINE_TYPE_OUT: + case SPECASM_LINE_TYPE_OR: + case SPECASM_LINE_TYPE_SBC: + case SPECASM_LINE_TYPE_SUB: + case SPECASM_LINE_TYPE_XOR: +#ifdef SPECASM_TARGET_NEXT_OPCODES + if ((line->type == SPECASM_LINE_TYPE_ADD) && + (line->data.op_code[0] == 0xED)) + prv_eval_equ_16bit_e(line, obj, line_no, 2); + else + prv_eval_equ_8bit_e(line, obj, line_no, 1); +#else + prv_eval_equ_8bit_e(line, obj, line_no, 1); +#endif + break; + case SPECASM_LINE_TYPE_RST: + exp = prv_eval_equ_label(line, obj, line_no, + line->data.op_code[1]); + if (err_type != SPECASM_ERROR_OK) + return; + if ((exp > 0x38) || (exp & 7)) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d bad argument to rst :%d", obj->fname, + line_no, exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + line->data.op_code[0] |= exp; + break; + case SPECASM_LINE_TYPE_LD: + opcode0 = line->data.op_code[0]; + if ((opcode0 & 0xC7) == 0x6) { + prv_eval_equ_8bit_e(line, obj, line_no, 1); + break; + } + switch (opcode0) { + case 0x1: + case 0x11: + case 0x21: + case 0x31: + case 0x2a: + case 0x3a: + case 0x22: + case 0x32: + prv_eval_equ_16bit_e(line, obj, line_no, 1); + break; + case 0xDD: + case 0xED: + case 0xFD: + prv_eval_equ_16bit_e(line, obj, line_no, 2); + break; + } + break; + case SPECASM_LINE_TYPE_BIT: + case SPECASM_LINE_TYPE_RES: + case SPECASM_LINE_TYPE_SET: + exp = prv_eval_equ_label(line, obj, line_no, + line->data.op_code[2]); + if (err_type != SPECASM_ERROR_OK) + return; + if (exp > 7) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d bad bit position :%d", obj->fname, + line_no, exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + line->data.op_code[1] |= ((uint8_t)exp) << 3; + break; + case SPECASM_LINE_TYPE_IM: + exp = prv_eval_equ_label(line, obj, line_no, + line->data.op_code[2]); + if (err_type != SPECASM_ERROR_OK) + return; + if (exp > 2) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d bad arg for im :%d", obj->fname, + line_no, exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + if (exp == 2) + line->data.op_code[1] = 0x5e; + else if (exp == 1) + line->data.op_code[1] = 0x56; + break; + case SPECASM_LINE_TYPE_CALL: + case SPECASM_LINE_TYPE_JP: + prv_eval_equ_16bit_e(line, obj, line_no, 1); + break; + case SPECASM_LINE_TYPE_DB: + prv_eval_equ_8bit_e(line, obj, line_no, 0); + break; + case SPECASM_LINE_TYPE_DW: + prv_eval_equ_16bit_e(line, obj, line_no, 0); + break; +#ifdef SPECASM_TARGET_NEXT_OPCODES + case SPECASM_LINE_TYPE_TEST: + case SPECASM_LINE_TYPE_NEXTREG: + prv_eval_equ_8bit_e(line, obj, line_no, 2); + break; + case SPECASM_LINE_TYPE_PUSH: + prv_eval_equ_push_imm_e(line, obj, line_no); + break; +#endif + default: + snprintf(error_buf, sizeof(error_buf), + "%s:%d unexpected expression", obj->fname, line_no); + err_type = SALINK_ERROR_UNEXPECTED_EXP; + break; + } + + specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); +} diff --git a/src/expression.h b/src/expression.h index 131a30e..c0dfd32 100644 --- a/src/expression.h +++ b/src/expression.h @@ -21,7 +21,8 @@ #include "salink.h" -int16_t salink_equ_eval_e(salink_obj_t *obj, const char *str, uint16_t line_no); +void salink_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no); void salink_equ_eval_global_e(salink_obj_t *obj, salink_global_t *global, salink_label_t *label, uint8_t depth); diff --git a/src/line.h b/src/line.h index fa1ea29..92c772a 100644 --- a/src/line.h +++ b/src/line.h @@ -173,6 +173,8 @@ #define SPECASM_LINE_TYPE_INC_LONG 143 #define SPECASM_LINE_TYPE_INC_SYS_SHORT 144 #define SPECASM_LINE_TYPE_INC_SYS_LONG 145 +#define SPECASM_LINE_TYPE_INC_BIN_SHORT 146 +#define SPECASM_LINE_TYPE_INC_BIN_LONG 147 #define SPECASM_LINE_TYPE_EXP_ADJ 160 diff --git a/src/link_obj.c b/src/link_obj.c index 9ec71c8..0d0a45c 100644 --- a/src/link_obj.c +++ b/src/link_obj.c @@ -31,7 +31,8 @@ static size_t label_count; static size_t main_index = SIZE_MAX; static uint16_t start_address = 0x8000; -static specasm_dirent_t dirent; +static void prv_add_queued_filename_e(const char *base, const char *prefix, + const char *str); static int prv_check_file(const char *fname) { @@ -133,27 +134,6 @@ static void prv_resolve_address_e(salink_obj_t *obj, specasm_line_t *line, *addr = label->data.off; } -static int16_t prv_eval_exp_from_id_e(salink_obj_t *obj, unsigned int line_no, - uint8_t id, uint8_t lng) -{ - const char *str; - int16_t exp; - - str = salink_get_label_str_e(id, lng); - if (err_type != SPECASM_ERROR_OK) - return 0; - - exp = salink_equ_eval_e(obj, str, line_no); - - if ((err_type != SPECASM_ERROR_OK) && (err_type < SPECASM_MAX_ERRORS)) { - snprintf(error_buf, sizeof(error_buf), "%s:%d %s", obj->fname, - line_no, specasm_error_msg(err_type)); - err_type = SALINK_ERROR_BAD_EXP; - } - - return exp; -} - static void prv_resolve_relative_address_e(salink_obj_t *obj, specasm_line_t *line, unsigned int i, uint16_t offset) @@ -183,6 +163,69 @@ static void prv_resolve_relative_address_e(salink_obj_t *obj, line->data.op_code[1] = (int8_t)(diff - 2); } +static void prv_flush_write_buf_e(specasm_handle_t out_f) +{ + if (buf_count > 0) { + specasm_file_write_e(out_f, buf.file_buf, buf_count); + if (err_type != SPECASM_ERROR_OK) + return; + bin_size += buf_count; + buf_count = 0; + } +} + +static uint16_t prv_write_bin_file_e(specasm_handle_t out_f, + specasm_line_t *line) +{ + specasm_handle_t in_f; + size_t read; + specasm_error_t err; + const char *fname; + uint16_t file_size = 0; + + /* + * Flush the buffer to make things a bit simpler. + */ + + prv_flush_write_buf_e(out_f); + if (err_type != SPECASM_ERROR_OK) + return 0; + + fname = salink_get_label_str_e(line->data.label, line->type); + if (err_type != SPECASM_ERROR_OK) + return 0; + + in_f = specasm_file_ropen_e(fname); + if (err_type != SPECASM_ERROR_OK) { + snprintf(error_buf, sizeof(error_buf), "Can't open %s", fname); + err_type = SALINK_ERROR_CANT_OPEN; + return 0; + } + + do { + read = specasm_file_read_e(in_f, buf.file_buf, MAX_BUFFER_SIZE); + if (err_type != SPECASM_ERROR_OK) + break; + + file_size += read; + if (read < MAX_BUFFER_SIZE) { + buf_count = read; + break; + } + + buf_count = MAX_BUFFER_SIZE; + prv_flush_write_buf_e(out_f); + if (err_type != SPECASM_ERROR_OK) + break; + } while (1); + + err = err_type; + specasm_file_close_e(in_f); + err_type = err; + + return file_size; +} + static void prv_write_line_e(specasm_handle_t f, specasm_line_t *line, uint16_t size) { @@ -192,12 +235,10 @@ static void prv_write_line_e(specasm_handle_t f, specasm_line_t *line, const char *data; if ((buf_count + size > MAX_BUFFER_SIZE) || - ((buf_count > 0) && (line->type == SPECASM_LINE_TYPE_DS))) { - specasm_file_write_e(f, buf.file_buf, buf_count); + (line->type == SPECASM_LINE_TYPE_DS)) { + prv_flush_write_buf_e(f); if (err_type != SPECASM_ERROR_OK) return; - bin_size += buf_count; - buf_count = 0; } if (line->type <= SPECASM_LINE_TYPE_SIMPLE_MAX) { @@ -215,11 +256,10 @@ static void prv_write_line_e(specasm_handle_t f, specasm_line_t *line, buf_count = to_set; return; } - specasm_file_write_e(f, buf.file_buf, MAX_BUFFER_SIZE); + buf_count = MAX_BUFFER_SIZE; + prv_flush_write_buf_e(f); if (err_type != SPECASM_ERROR_OK) return; - bin_size += MAX_BUFFER_SIZE; - buf_count = 0; to_set = size < MAX_BUFFER_SIZE ? size : MAX_BUFFER_SIZE; } while (1); @@ -312,203 +352,6 @@ static uint16_t prv_align_e(specasm_handle_t f, uint16_t align) return adjust; } -static int16_t prv_eval_equ_label(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no, uint8_t id) -{ - uint8_t label_type; - uint8_t lng; - - label_type = specasm_line_get_addr_type(line); - lng = label_type == SPECASM_FLAGS_ADDR_SHORT ? 0 : 1; - return prv_eval_exp_from_id_e(obj, line_no, id, lng); -} - -static void prv_eval_equ_8bit_e(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no, uint8_t loc) -{ - int16_t exp; - - exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); - if (err_type != SPECASM_ERROR_OK) - return; - - if ((exp > 255) || (exp < -128)) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d immediate too big :%d", obj->fname, line_no, - exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - line->data.op_code[loc] = exp; -} - -static void prv_eval_equ_16bit_e(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no, uint8_t loc) -{ - int16_t exp; - - exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); - if (err_type != SPECASM_ERROR_OK) - return; - - *((uint16_t *)&line->data.op_code[loc]) = exp; -} - -#ifdef SPECASM_TARGET_NEXT_OPCODES -static void prv_eval_equ_push_imm_e(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no) -{ - int16_t exp; - uint8_t exp_rev[2]; - - /* - * The push imm instruction is weird in that the immediate is encoded - * in big endian format. If we're dealing with an expression this means - * that the expression id will be in byte 3 and not byte 2 as one might - * expect. - * - * We also need to make sure we reverse the bytes of the evaluated - * expression. - */ - - exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[3]); - if (err_type != SPECASM_ERROR_OK) - return; - memcpy(exp_rev, &exp, 2); - line->data.op_code[2] = exp_rev[1]; - line->data.op_code[3] = exp_rev[0]; -} -#endif - -static void prv_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no) -{ - int16_t exp; - uint8_t opcode0; - - line->type -= SPECASM_LINE_TYPE_EXP_ADJ; - - switch (line->type) { - case SPECASM_LINE_TYPE_ADC: - case SPECASM_LINE_TYPE_ADD: - case SPECASM_LINE_TYPE_AND: - case SPECASM_LINE_TYPE_CP: - case SPECASM_LINE_TYPE_IN: - case SPECASM_LINE_TYPE_OUT: - case SPECASM_LINE_TYPE_OR: - case SPECASM_LINE_TYPE_SBC: - case SPECASM_LINE_TYPE_SUB: - case SPECASM_LINE_TYPE_XOR: -#ifdef SPECASM_TARGET_NEXT_OPCODES - if ((line->type == SPECASM_LINE_TYPE_ADD) && - (line->data.op_code[0] == 0xED)) - prv_eval_equ_16bit_e(line, obj, line_no, 2); - else - prv_eval_equ_8bit_e(line, obj, line_no, 1); -#else - prv_eval_equ_8bit_e(line, obj, line_no, 1); -#endif - break; - case SPECASM_LINE_TYPE_RST: - exp = prv_eval_equ_label(line, obj, line_no, - line->data.op_code[1]); - if (err_type != SPECASM_ERROR_OK) - return; - if ((exp > 0x38) || (exp & 7)) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d bad argument to rst :%d", obj->fname, - line_no, exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - line->data.op_code[0] |= exp; - break; - case SPECASM_LINE_TYPE_LD: - opcode0 = line->data.op_code[0]; - if ((opcode0 & 0xC7) == 0x6) { - prv_eval_equ_8bit_e(line, obj, line_no, 1); - break; - } - switch (opcode0) { - case 0x1: - case 0x11: - case 0x21: - case 0x31: - case 0x2a: - case 0x3a: - case 0x22: - case 0x32: - prv_eval_equ_16bit_e(line, obj, line_no, 1); - break; - case 0xDD: - case 0xED: - case 0xFD: - prv_eval_equ_16bit_e(line, obj, line_no, 2); - break; - } - break; - case SPECASM_LINE_TYPE_BIT: - case SPECASM_LINE_TYPE_RES: - case SPECASM_LINE_TYPE_SET: - exp = prv_eval_equ_label(line, obj, line_no, - line->data.op_code[2]); - if (err_type != SPECASM_ERROR_OK) - return; - if (exp > 7) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d bad bit position :%d", obj->fname, - line_no, exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - line->data.op_code[1] |= ((uint8_t)exp) << 3; - break; - case SPECASM_LINE_TYPE_IM: - exp = prv_eval_equ_label(line, obj, line_no, - line->data.op_code[2]); - if (err_type != SPECASM_ERROR_OK) - return; - if (exp > 2) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d bad arg for im :%d", obj->fname, - line_no, exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - if (exp == 2) - line->data.op_code[1] = 0x5e; - else if (exp == 1) - line->data.op_code[1] = 0x56; - break; - case SPECASM_LINE_TYPE_CALL: - case SPECASM_LINE_TYPE_JP: - prv_eval_equ_16bit_e(line, obj, line_no, 1); - break; - case SPECASM_LINE_TYPE_DB: - prv_eval_equ_8bit_e(line, obj, line_no, 0); - break; - case SPECASM_LINE_TYPE_DW: - prv_eval_equ_16bit_e(line, obj, line_no, 0); - break; -#ifdef SPECASM_TARGET_NEXT_OPCODES - case SPECASM_LINE_TYPE_TEST: - case SPECASM_LINE_TYPE_NEXTREG: - prv_eval_equ_8bit_e(line, obj, line_no, 2); - break; - case SPECASM_LINE_TYPE_PUSH: - prv_eval_equ_push_imm_e(line, obj, line_no); - break; -#endif - default: - snprintf(error_buf, sizeof(error_buf), - "%s:%d unexpected expression", obj->fname, line_no); - err_type = SALINK_ERROR_UNEXPECTED_EXP; - break; - } - - specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); -} - static uint8_t prv_add_label_e(salink_obj_t *obj, uint16_t size, uint8_t type, uint8_t id, uint16_t line_no) { @@ -609,11 +452,81 @@ static void prv_add_align_e(salink_obj_t *obj, specasm_line_t *line, label->data.off = size; } -static void prv_add_queued_file_e(const char *base, const char *prefix, - specasm_line_t *line) +static void prv_stat_fname_e(const char *fname, specasm_stat_t *stat_buf) +{ + specasm_handle_t in_f; + specasm_error_t err; + + in_f = specasm_file_ropen_e(fname); + if (err_type != SPECASM_ERROR_OK) + return; + + specasm_file_stat_e(in_f, stat_buf); + err = err_type; + specasm_file_close_e(in_f); + err_type = err; +} + +static uint8_t prv_add_queued_dir_e(const char *fname) +{ + specasm_dir_t dir; + specasm_dirent_t dirent; + size_t fname_len; + char dir_name[MAX_FNAME + 1]; + + /* + * If we can't stat the filename we'll assume that it's just a .x file + * without the extension. If it isn't will catch the error later. + */ + + if (!specasm_file_isdir(fname)) + return 0; + + fname_len = strlen(fname); + if (fname_len >= MAX_FNAME) { + err_type = SPECASM_ERROR_BAD_FNAME; + return 0; + } + dir = specasm_opendir_e(fname); + if (err_type != SPECASM_ERROR_OK) { + strcpy(error_buf, "Failed to read directory"); + err_type = SALINK_ERROR_READDIR; + return 0; + } + + strcpy(dir_name, fname); + dir_name[fname_len] = '/'; + dir_name[fname_len + 1] = 0; + + while (specasm_readdir(dir, &dirent)) { + + /* + * Make sure we only add .x files and not .x directories + * or any directories for that matter. Subdirectories need + * to be explicitly added with a '-' or a '+' directive. + */ + + if (specasm_isdirent_dir(dirent)) + continue; + if (!prv_check_file(specasm_getdirname(dirent))) + continue; + prv_add_queued_filename_e(dir_name, "", + specasm_getdirname(dirent)); + if (err_type != SPECASM_ERROR_OK) { + specasm_closedir(dir); + return 0; + } + } + specasm_closedir(dir); + + err_type = SPECASM_ERROR_OK; + + return 1; +} + +static void prv_add_queued_filename_e(const char *base, const char *prefix, + const char *str) { - const char *str; - uint8_t id; int space_needed; int prefix_len; int base_len; @@ -628,8 +541,23 @@ static void prv_add_queued_file_e(const char *base, const char *prefix, return; } - id = line->data.label; - str = salink_get_label_str_e(id, line->type); + /* + * Build up a path relative to the including path, providing + * it's not a complete path. So if the including file is + * one/two.x and it includes does - three.x, then we get + * + * one/three.x + * + * If the including file is simple two.x then we get + * + * three.x + * + * If the including file is one/two/ we'd get + * + * one/two/three.x + * + * TODO: Need to update this to cope with drive letters. + */ base_len = 0; if (str[0] != '/') { @@ -645,7 +573,7 @@ static void prv_add_queued_file_e(const char *base, const char *prefix, return; } - ptr = &buf.fname[queued_files++][0]; + ptr = &buf.fname[queued_files][0]; start = ptr; if (base_len) { strncpy(ptr, base, base_len); @@ -656,6 +584,18 @@ static void prv_add_queued_file_e(const char *base, const char *prefix, ptr += prefix_len; } strcpy(ptr, str); + + /* + * Check to see whether this is a directory or not. + * If so we add the files in this directory and return. + */ + + if (prv_add_queued_dir_e(start)) + return; + if (err_type != SPECASM_ERROR_OK) + return; + + queued_files++; if (start[space_needed - 2] != '.' && (start[space_needed - 1] | 32) != 'x') { if (space_needed + 2 > MAX_FNAME) { @@ -668,6 +608,20 @@ static void prv_add_queued_file_e(const char *base, const char *prefix, } } +static void prv_add_queued_file_e(const char *base, const char *prefix, + specasm_line_t *line) +{ + uint8_t id; + const char *str; + + id = line->data.label; + str = salink_get_label_str_e(id, line->type); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_add_queued_filename_e(base, prefix, str); +} + static void prv_add_equ_label_e(specasm_line_t *line, salink_obj_t *obj, uint16_t line_no) { @@ -710,6 +664,37 @@ static void prv_add_equ_label_e(specasm_line_t *line, salink_obj_t *obj, label->type = SALINK_LABEL_TYPE_EQU_GLOBAL; } +static uint16_t prv_inc_bin_size_e(specasm_line_t *line, uint16_t size) +{ + const char *fname; + specasm_stat_t stat_buf; + uint32_t bin_size; + uint32_t new_size; + + fname = salink_get_label_str_e(line->data.label, line->type); + if (err_type != SPECASM_ERROR_OK) + return 0; + + prv_stat_fname_e(fname, &stat_buf); + if (err_type != SPECASM_ERROR_OK) { + snprintf(error_buf, sizeof(error_buf), "Can't open or stat %s", + fname); + err_type = SALINK_ERROR_CANT_OPEN; + return 0; + } + + bin_size = specasm_get_file_size(&stat_buf); + new_size = bin_size + size; + + if ((new_size < bin_size) || (bin_size > 0xffff)) { + snprintf(error_buf, sizeof(error_buf), "no room for %s", fname); + err_type = SALINK_ERROR_PROGRAM_TOO_BIG; + return 0; + } + + return (uint16_t)new_size; +} + static void prv_parse_obj_e(const char *fname) { uint16_t i; @@ -735,7 +720,7 @@ static void prv_parse_obj_e(const char *fname) strcpy(obj->fname, fname); obj->label_start = label_count; - specasm_load_e(fname); + specasm_load_e(obj->fname); if (err_type != SPECASM_ERROR_OK) { snprintf(error_buf, sizeof(error_buf), "Can't open %s", fname); err_type = SALINK_ERROR_CANT_OPEN; @@ -771,7 +756,10 @@ static void prv_parse_obj_e(const char *fname) prv_add_queued_file_e(obj->fname, "", line); } else if ((line->type >= SPECASM_LINE_TYPE_INC_SYS_SHORT) && (line->type <= SPECASM_LINE_TYPE_INC_SYS_LONG)) { - prv_add_queued_file_e(fname, "/specasm/", line); + prv_add_queued_file_e(obj->fname, "/specasm/", line); + } else if ((line->type == SPECASM_LINE_TYPE_INC_BIN_SHORT) || + (line->type == SPECASM_LINE_TYPE_INC_BIN_LONG)) { + size = prv_inc_bin_size_e(line, size); } else { size += specasm_compute_line_size(line); } @@ -802,16 +790,23 @@ static void prv_process_queued_files_e(void) } } +static int prv_obj_file_cmp(const void *a, const void *b) +{ + const uint8_t *a_i = (const uint8_t *)a; + const uint8_t *b_i = (const uint8_t *)b; + + return strcmp(obj_files[*a_i].fname, obj_files[*b_i].fname); +} + static uint8_t prv_order_objects_e(void) { - salink_obj_t tmp; - uint8_t loaded; uint8_t i; - salink_global_t *glob; /* * We want to put the object file with the Main label - * first. + * first. The remaining object files are placed in + * ascending order of their file names before being + * written out to the final binary. */ if (main_index == SIZE_MAX) { @@ -820,23 +815,48 @@ static uint8_t prv_order_objects_e(void) return 0; } - loaded = main_index == (obj_file_count - 1); - if (main_index > 0) { - memcpy(&tmp, &obj_files[0], sizeof(obj_files[0])); - memcpy(&obj_files[0], &obj_files[main_index], - sizeof(obj_files[0])); - memcpy(&obj_files[main_index], &tmp, sizeof(obj_files[0])); - for (i = 0; i < global_count; i++) { - glob = &globals[i]; - if (glob->obj_index == 0) - glob->obj_index = main_index; - else if (glob->obj_index == main_index) - glob->obj_index = 0; - } - main_index = 0; + for (i = 0; i < obj_file_count; i++) + obj_files_order[i] = i; + + if (main_index != 0) { + obj_files_order[main_index] = 0; + obj_files_order[0] = main_index; + } + + if (obj_file_count > 2) { + qsort(&obj_files_order[1], obj_file_count - 1, sizeof(uint8_t), + prv_obj_file_cmp); } - return loaded; + return main_index == (obj_file_count - 1); +} + +static void prv_check_duplicate_objs_e(void) +{ + unsigned int i; + const char *fname1; + + /* + * It's not possible to include the main file twice as we'll get an + * error telling us that the .Main label has been defined multiple + * times. Probably not worth the extra code to detect this case and + * report this error instead. + */ + + /* + * We rely on the fact that all but the first object file are sorted + * in ascending alphabetical order, so we only need obj_file_count - 2 + * comparisons. + */ + + for (i = 1; i < obj_file_count - 1; i++) { + fname1 = obj_files[obj_files_order[i]].fname; + if (!strcmp(fname1, obj_files[obj_files_order[i + 1]].fname)) { + err_type = SALINK_ERROR_DUP_OBJ_FILE; + sprintf(error_buf, "%s included twice!", fname1); + return; + } + } } static void prv_complete_absolutes_e(void) @@ -851,7 +871,7 @@ static void prv_complete_absolutes_e(void) uint16_t real_off = start_address; for (i = 0; i < obj_file_count; i++) { - obj = &obj_files[i]; + obj = &obj_files[obj_files_order[i]]; for (j = obj->label_start; j < obj->label_end; j++) { label = &labels[j]; @@ -914,7 +934,7 @@ static uint16_t prv_link_obj_e(specasm_handle_t f, salink_obj_t *obj, line = &state.lines.lines[i]; if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) - prv_apply_expressions_e(line, obj, i); + salink_apply_expressions_e(line, obj, i); switch (line->type) { case SPECASM_LINE_TYPE_ALIGN: @@ -954,6 +974,10 @@ static uint16_t prv_link_obj_e(specasm_handle_t f, salink_obj_t *obj, case SPECASM_LINE_TYPE_DJNZ: prv_resolve_relative_address_e(obj, line, i, offset); break; + case SPECASM_LINE_TYPE_INC_BIN_SHORT: + case SPECASM_LINE_TYPE_INC_BIN_LONG: + offset += prv_write_bin_file_e(f, line); + continue; default: break; } @@ -978,13 +1002,13 @@ static void prv_link_e(uint8_t main_loaded) unsigned int i; specasm_handle_t f; uint16_t offset = start_address; - salink_obj_t *obj = &obj_files[0]; + salink_obj_t *obj = &obj_files[main_index]; f = specasm_file_wopen_e(image_name); if (err_type != SPECASM_ERROR_OK) return; for (i = 0; i < obj_file_count; i++) { - obj = &obj_files[i]; + obj = &obj_files[obj_files_order[i]]; if (i > 0 || !main_loaded) { specasm_load_e(obj->fname); if (err_type != SPECASM_ERROR_OK) @@ -1031,6 +1055,7 @@ static void prv_salink_e(void) { uint8_t main_loaded; char ibuf[16]; + specasm_dirent_t dirent; specasm_dir_t dir = specasm_opendir_e("."); if (err_type != SPECASM_ERROR_OK) { @@ -1039,6 +1064,8 @@ static void prv_salink_e(void) return; } while (specasm_readdir(dir, &dirent)) { + if (specasm_isdirent_dir(dirent)) + continue; if (!prv_check_file(specasm_getdirname(dirent))) continue; prv_parse_obj_e(specasm_getdirname(dirent)); @@ -1065,6 +1092,10 @@ static void prv_salink_e(void) if (err_type != SPECASM_ERROR_OK) return; + prv_check_duplicate_objs_e(); + if (err_type != SPECASM_ERROR_OK) + return; + prv_complete_absolutes_e(); if (err_type != SPECASM_ERROR_OK) return; diff --git a/src/peer_file.h b/src/peer_file.h index af87da7..fa88c4d 100644 --- a/src/peer_file.h +++ b/src/peer_file.h @@ -30,16 +30,18 @@ typedef struct esxdos_stat specasm_stat_t; #define specasm_getdirname(d) (&d.dir[1]) #define specasm_remove_file(f) (void)esxdos_f_unlink(f) #define specasm_get_file_size(stat_buf) (stat_buf)->size +#define specasm_isdirent_dir(d) ((d).dir[0] & __esx_dir_a_dir) #else #include -typedef struct esxdos_dirent specasm_dirent_t; +typedef struct esx_dirent specasm_dirent_t; typedef struct esx_stat specasm_stat_t; #define SPECASM_PATH_MAX ESX_PATHNAME_MAX #define specasm_readdir(dir, drent) esx_f_readdir(dir, drent) #define specasm_closedir(dir) esx_f_close(dir) -#define specasm_getdirname(d) (&d.dir[1]) +#define specasm_getdirname(d) ((d).name) #define specasm_remove_file(f) (void)esx_f_unlink(f) #define specasm_get_file_size(stat_buf) (stat_buf)->size +#define specasm_isdirent_dir(d) ((d).attr & ESX_DIR_A_DIR) #endif #else @@ -61,6 +63,7 @@ uint8_t specasm_readdir(specasm_dir_t dir, specasm_dirent_t *dirent); #define specasm_remove_file(f) (void)remove(f) #define specasm_f_stat(f, stat_buf) fstat(f, stat_buf) #define specasm_get_file_size(stat_buf) (stat_buf)->st_size +#define specasm_isdirent_dir(d) ((d).d_type & DT_DIR) #endif specasm_handle_t specasm_file_wopen_e(const char *fname); @@ -70,5 +73,6 @@ size_t specasm_file_read_e(specasm_handle_t f, void *data, size_t size); void specasm_file_close_e(specasm_handle_t f); specasm_dir_t specasm_opendir_e(const char *fname); void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf); +uint8_t specasm_file_isdir(const char *fname); #endif diff --git a/src/peer_file_next.c b/src/peer_file_next.c index 5a74233..a140e65 100644 --- a/src/peer_file_next.c +++ b/src/peer_file_next.c @@ -14,15 +14,18 @@ * limitations under the License. */ -#include "peer_file.h" +#include + #include "error.h" +#include "peer_file.h" specasm_handle_t specasm_file_wopen_e(const char *fname) { specasm_handle_t f; + errno = 0; f = esx_f_open(fname, ESX_MODE_W | ESX_MODE_OPEN_CREAT_TRUNC); - if (!f) + if (errno) err_type = SPECASM_ERROR_OPEN; return f; @@ -32,8 +35,9 @@ specasm_handle_t specasm_file_ropen_e(const char *fname) { specasm_handle_t f; + errno = 0; f = esx_f_open(fname, ESX_MODE_R); - if (!f) + if (errno) err_type = SPECASM_ERROR_OPEN; return f; @@ -41,7 +45,9 @@ specasm_handle_t specasm_file_ropen_e(const char *fname) void specasm_file_write_e(specasm_handle_t f, const void *data, size_t size) { - if (esx_f_write(f, (void *)data, size) == 0) + errno = 0; + (void)esx_f_write(f, (void *)data, size); + if (errno) err_type = SPECASM_ERROR_WRITE; } @@ -55,12 +61,15 @@ size_t specasm_file_read_e(specasm_handle_t f, void *data, size_t size) return esx_f_read(f, data, size); } -void specasm_file_close_e(specasm_handle_t f) { (void)esxdos_f_close(f); } +void specasm_file_close_e(specasm_handle_t f) { (void)esx_f_close(f); } specasm_dir_t specasm_opendir_e(const char *fname) { - specasm_dir_t d = esx_f_opendir(fname); - if (!d) + specasm_dir_t d; + + errno = 0; + d = esx_f_opendir(fname); + if (errno) err_type = SPECASM_ERROR_OPEN; return d; @@ -68,6 +77,22 @@ specasm_dir_t specasm_opendir_e(const char *fname) void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf) { - if (esx_f_fstat(f, buf)) + errno = 0; + (void)esx_f_fstat(f, buf); + if (errno) err_type = SPECASM_ERROR_READ; } + +uint8_t specasm_file_isdir(const char *fname) +{ + specasm_dir_t d; + + errno = 0; + d = esx_f_opendir(fname); + if (errno) + return 0; + + (void)esx_f_close(d); + + return 1; +} diff --git a/src/peer_file_posix.c b/src/peer_file_posix.c index d6e9784..d99034e 100644 --- a/src/peer_file_posix.c +++ b/src/peer_file_posix.c @@ -87,3 +87,21 @@ void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf) if (fstat(fileno(f), buf)) err_type = SPECASM_ERROR_READ; } + +uint8_t specasm_file_isdir(const char *fname) +{ + specasm_handle_t f; + specasm_stat_t buf; + int res; + + f = fopen(fname, "r"); + if (!f) + return 0; + + res = fstat(fileno(f), &buf); + (void)fclose(f); + if (res) + return 0; + + return buf.st_mode & S_IFDIR ? 1 : 0; +} diff --git a/src/peer_file_zx.c b/src/peer_file_zx.c index c3bd35e..e1ca7d5 100644 --- a/src/peer_file_zx.c +++ b/src/peer_file_zx.c @@ -14,15 +14,18 @@ * limitations under the License. */ -#include "peer_file.h" +#include + #include "error.h" +#include "peer_file.h" specasm_handle_t specasm_file_wopen_e(const char *fname) { specasm_handle_t f; + errno = 0; f = esxdos_f_open(fname, ESXDOS_MODE_W | ESXDOS_MODE_CT); - if (!f) + if (errno) err_type = SPECASM_ERROR_OPEN; return f; @@ -32,8 +35,9 @@ specasm_handle_t specasm_file_ropen_e(const char *fname) { specasm_handle_t f; + errno = 0; f = esxdos_f_open(fname, ESXDOS_MODE_R); - if (!f) + if (errno) err_type = SPECASM_ERROR_OPEN; return f; @@ -41,7 +45,9 @@ specasm_handle_t specasm_file_ropen_e(const char *fname) void specasm_file_write_e(specasm_handle_t f, const void *data, size_t size) { - if (esxdos_f_write(f, (void *)data, size) == 0) + errno = 0; + (void)esxdos_f_write(f, (void *)data, size); + if (errno) err_type = SPECASM_ERROR_WRITE; } @@ -59,8 +65,11 @@ void specasm_file_close_e(specasm_handle_t f) { (void)esxdos_f_close(f); } specasm_dir_t specasm_opendir_e(const char *fname) { - specasm_dir_t d = esxdos_f_opendir(fname); - if (!d) + specasm_dir_t d; + + errno = 0; + d = esxdos_f_opendir(fname); + if (errno) err_type = SPECASM_ERROR_OPEN; return d; @@ -68,6 +77,22 @@ specasm_dir_t specasm_opendir_e(const char *fname) void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf) { - if (esxdos_f_fstat(f, buf) < 0) + errno = 0; + (void)esxdos_f_fstat(f, buf); + if (errno) err_type = SPECASM_ERROR_READ; } + +uint8_t specasm_file_isdir(const char *fname) +{ + specasm_handle_t f; + + errno = 0; + f = esxdos_f_open(fname, ESXDOS_MODE_R); + if (!errno) { + (void)esxdos_f_close(f); + return 0; + } + + return errno == ESXDOS_EISDIR; +} diff --git a/src/peer_next.c b/src/peer_next.c index 0cdc1b9..47a1fba 100644 --- a/src/peer_next.c +++ b/src/peer_next.c @@ -18,24 +18,28 @@ #include "state.h" #include +#include #include void specasm_peer_write_state_e(const char *fname, uint16_t checksum) { unsigned char f; + errno = 0; f = esx_f_open(fname, ESX_MODE_W | ESX_MODE_OPEN_CREAT); - if (!f) { + if (errno) { err_type = SPECASM_ERROR_OPEN; return; } - if (esx_f_write(f, &state, sizeof(state)) == 0) { + (void)esx_f_write(f, &state, sizeof(state)); + if (errno) { err_type = SPECASM_ERROR_WRITE; goto cleanup; } - if (esx_f_write(f, &checksum, sizeof(checksum)) == 0) + (void)esx_f_write(f, &checksum, sizeof(checksum)); + if (errno) err_type = SPECASM_ERROR_WRITE; cleanup: @@ -47,8 +51,9 @@ uint16_t specasm_peer_read_state_e(const char *fname) unsigned char f; uint16_t checksum = 0; + errno = 0; f = esx_f_open(fname, ESX_MODE_R); - if (!f) { + if (errno) { err_type = SPECASM_ERROR_OPEN; return 0; } diff --git a/src/peer_zx.c b/src/peer_zx.c index 2543573..4650162 100644 --- a/src/peer_zx.c +++ b/src/peer_zx.c @@ -18,24 +18,28 @@ #include "state.h" #include +#include #include void specasm_peer_write_state_e(const char *fname, uint16_t checksum) { unsigned char f; + errno = 0; f = esxdos_f_open(fname, ESXDOS_MODE_W | ESXDOS_MODE_OC); - if (!f) { + if (errno) { err_type = SPECASM_ERROR_OPEN; return; } - if (esxdos_f_write(f, &state, sizeof(state)) == 0) { + (void)esxdos_f_write(f, &state, sizeof(state)); + if (errno) { err_type = SPECASM_ERROR_WRITE; goto cleanup; } - if (esxdos_f_write(f, &checksum, sizeof(checksum)) == 0) + (void)esxdos_f_write(f, &checksum, sizeof(checksum)); + if (errno) err_type = SPECASM_ERROR_WRITE; cleanup: @@ -47,8 +51,9 @@ uint16_t specasm_peer_read_state_e(const char *fname) unsigned char f; uint16_t checksum = 0; + errno = 0; f = esxdos_f_open(fname, ESXDOS_MODE_R); - if (!f) { + if (errno) { err_type = SPECASM_ERROR_OPEN; return 0; } diff --git a/src/salink.c b/src/salink.c index e074b44..e34be43 100644 --- a/src/salink.c +++ b/src/salink.c @@ -40,6 +40,7 @@ char map_name[MAX_FNAME + 1]; salink_label_t labels[MAX_LABELS]; salink_obj_t obj_files[MAX_FILES]; unsigned int obj_file_count; +uint8_t obj_files_order[MAX_FILES]; salink_global_t globals[MAX_GLOBALS]; unsigned int global_count; diff --git a/src/salink.h b/src/salink.h index ec59366..5bad67d 100644 --- a/src/salink.h +++ b/src/salink.h @@ -61,6 +61,7 @@ #define SALINK_ERROR_UNEXPECTED_EXP (SPECASM_MAX_ERRORS + 16) #define SALINK_ERROR_DIV_ZERO (SPECASM_MAX_ERRORS + 17) #define SALINK_ERROR_BAD_EXPRESSION (SPECASM_MAX_ERRORS + 18) +#define SALINK_ERROR_DUP_OBJ_FILE (SPECASM_MAX_ERRORS + 19) /* * Label usage for EQU statements @@ -136,6 +137,7 @@ extern char map_name[MAX_FNAME + 1]; extern unsigned int global_count; extern salink_obj_t obj_files[MAX_FILES]; extern unsigned int obj_file_count; +extern uint8_t obj_files_order[MAX_FILES]; extern char image_name[MAX_FNAME + 1]; extern unsigned int bin_size; #endif diff --git a/src/salink_trampolines.c b/src/salink_trampolines.c index 72c40ab..86ae15a 100644 --- a/src/salink_trampolines.c +++ b/src/salink_trampolines.c @@ -25,8 +25,8 @@ extern unsigned char _z_page_table[]; -int16_t salink_equ_eval_banked_e(salink_obj_t *obj, const char *str, - uint16_t line_no); +void salink_apply_expressions_banked_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no); void salink_equ_eval_global_banked_e(salink_obj_t *obj, salink_global_t *global, salink_label_t *label, uint8_t depth); void specasm_write_map_banked_e(void); @@ -50,20 +50,17 @@ void specasm_write_map_e(void) ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_LINK_OBJ_BANK]); } -int16_t salink_equ_eval_e(salink_obj_t *obj, const char *str, uint16_t line_no) +void salink_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no) { - int16_t v; - /* * Only called from the link_obj page, so we map it back * in once our call has finished. */ ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_EXPRESSION_BANK]); - v = salink_equ_eval_banked_e(obj, str, line_no); + salink_apply_expressions_banked_e(line, obj, line_no); ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_LINK_OBJ_BANK]); - - return v; } void salink_equ_eval_global_e(salink_obj_t *obj, salink_global_t *global, diff --git a/src/specasm.c b/src/specasm.c index 05ba57e..dd6b11b 100644 --- a/src/specasm.c +++ b/src/specasm.c @@ -23,6 +23,7 @@ #include "editor.h" #include "line.h" +#include "specasm_mainloop.h" #include "state.h" #define SPECASM_KEY_CALIBRATION 13 @@ -33,7 +34,7 @@ int main() uint8_t new_key; uint16_t i; uint8_t *ptr = (uint8_t *)23328; - uint16_t delay = ((200 / 11) * *ptr) / 10; + const uint16_t delay = ((200 / 11) * 11) / 10; specasm_init_dump_table(); @@ -46,27 +47,8 @@ int main() // Make cursor flash specasm_text_set_flash(col, line, FLASH); - do { - in_wait_key(); - k = in_inkey(); - if (!k) - continue; - do { - if (k == SPECASM_KEY_COMMAND) { - in_wait_nokey(); - new_key = in_inkey(); - } else { - for (i = 0; i < SPECASM_KEY_CALIBRATION; i++) { - specasm_sleep_ms(delay); - new_key = in_inkey(); - if (k != new_key) - break; - } - } - specasm_handle_key_press(k); - k = new_key; - } while (k); - } while (!quitting); + + specasm_main_loop(delay, SPECASM_KEY_CALIBRATION); return 0; } diff --git a/src/specasm_mainloop.c b/src/specasm_mainloop.c new file mode 100644 index 0000000..a21e572 --- /dev/null +++ b/src/specasm_mainloop.c @@ -0,0 +1,77 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifdef SPECTRUM +#include +#else +#include +#endif +#include +#include + +#include "editor.h" +#include "specasm_mainloop.h" + +static uint8_t prv_calibration_loop(uint8_t k, uint16_t delay, + uint8_t calibration) +{ + uint8_t new_key; + uint8_t i; + uint16_t old_scan_code = in_key_scancode(k) & 0x3ff; + + for (i = 0; i < calibration; i++) { + specasm_sleep_ms(delay); + new_key = in_inkey(); + if (old_scan_code != (in_key_scancode(new_key) & 0x3ff)) + return new_key; + } + + return k; +} + +void specasm_main_loop(uint16_t delay, uint8_t calibration) +{ + uint8_t k; + uint8_t new_key; + + do { + in_wait_key(); + + /* + * Delay here is needed on the Toastrack to avoid + * phantom keys when pressing things like Delete or the + * arrow keys. + */ + + specasm_sleep_ms(delay / 2); + k = in_inkey(); + if (!k) + continue; + do { + new_key = prv_calibration_loop(k, delay, calibration); + specasm_handle_key_press(k); + + while (k == new_key) { + new_key = prv_calibration_loop(k, delay / 2, + calibration / 2); + if (k == new_key) + specasm_handle_key_press(k); + } + + k = new_key; + } while (k); + } while (!quitting); +} diff --git a/src/specasm_mainloop.h b/src/specasm_mainloop.h new file mode 100644 index 0000000..cce837d --- /dev/null +++ b/src/specasm_mainloop.h @@ -0,0 +1,24 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef SPECASM_MAINLOOP_H +#define SPECASM_MAINLOOP_H + +#include + +void specasm_main_loop(uint16_t delay, uint8_t calibration); + +#endif diff --git a/src/specasm_next.c b/src/specasm_next.c index 17277b1..2e3fa89 100644 --- a/src/specasm_next.c +++ b/src/specasm_next.c @@ -24,20 +24,18 @@ #include "editor.h" #include "line.h" +#include "specasm_mainloop.h" #include "state.h" #define SPECASM_KEY_CALIBRATION 13 void specasm_peer_next_copy_chars(void); -int main(void) +int main(int argc, char *argv[]) { - uint8_t k; - uint16_t delay = ((200 / 11) * 11) / 10; - uint8_t new_key; - uint16_t i; uint8_t turbo; struct esx_mode mode; + const uint16_t delay = ((200 / 11) * 11) / 10; /* * The editor is nicer to use at 28Mhz. We're running on @@ -58,34 +56,18 @@ int main(void) zx_cls(SPECASM_CODE_COLOUR | SPECASM_LABEL_BACKGROUND); err_type = SPECASM_ERROR_OK; - specasm_editor_reset(); + + if (argc > 1) + specasm_editor_preload(argv[1]); + else + specasm_editor_reset(); specasm_draw_status(); // Make cursor flash specasm_text_set_flash(col, line, FLASH); - do { - in_wait_key(); - k = in_inkey(); - if (!k) - continue; - do { - if (k == SPECASM_KEY_COMMAND) { - in_wait_nokey(); - new_key = in_inkey(); - } else { - for (i = 0; i < SPECASM_KEY_CALIBRATION; i++) { - specasm_sleep_ms(delay); - new_key = in_inkey(); - if (k != new_key) - break; - } - } - specasm_handle_key_press(k); - k = new_key; - } while (k); - } while (!quitting); + specasm_main_loop(delay, SPECASM_KEY_CALIBRATION); zx_cls(PAPER_WHITE | INK_WHITE); ZXN_WRITE_REG(REG_TURBO_MODE, turbo); diff --git a/src/specasm_trampolines.c b/src/specasm_trampolines.c index 232e55f..a93f7db 100644 --- a/src/specasm_trampolines.c +++ b/src/specasm_trampolines.c @@ -22,6 +22,7 @@ #define SPECASM_NEXT_PARSE_BANK ((43 << 1) + 1) #define SPECASM_NEXT_DUMP_BANK ((44 << 1) + 1) #define SPECASM_NEXT_PARSE_LD_BANK ((45 << 1) + 1) +#define SPECASM_NEXT_CLIP_BANK ((46 << 1) + 1) extern unsigned char _z_page_table[]; @@ -35,6 +36,10 @@ void specasm_init_dump_table_banked(void); uint8_t specasm_dump_opcode_banked_e(const specasm_line_t *line, char *buf); uint8_t specasm_parse_ld_banked_e(const char *args, specasm_line_t *line, const specasm_opcode_t *op_entry); +void specasm_clip_reset_banked(void); +void specasm_clip_add_line_banked_e(const char *line); +uint16_t specasm_clip_get_line_banked(uint16_t ptr, char *buffer); +uint16_t specasm_clip_get_line_count_banked(void); uint8_t specasm_parse_exp_e(const char *str, uint8_t *label1, uint8_t *label1_type) @@ -101,3 +106,27 @@ uint8_t specasm_parse_ld_e(const char *args, specasm_line_t *line, return e; } + +void specasm_clip_reset(void) +{ + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK]); + specasm_clip_reset_banked(); +} + +void specasm_clip_add_line_e(const char *line) +{ + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK]); + specasm_clip_add_line_banked_e(line); +} + +uint16_t specasm_clip_get_line(uint16_t ptr, char *buffer) +{ + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK]); + return specasm_clip_get_line_banked(ptr, buffer); +} + +uint16_t specasm_clip_get_line_count(void) +{ + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK]); + return specasm_clip_get_line_count_banked(); +} diff --git a/src/state_base.h b/src/state_base.h index 4a4d0bb..7aabff7 100644 --- a/src/state_base.h +++ b/src/state_base.h @@ -18,11 +18,11 @@ #define SPECASM_STATE_READ_H #ifdef SPECASM_TARGET_NEXT_OPCODES -#define SPECASM_VERSION 0x8007 -#define SPECASM_VERSION_STR "v7n" +#define SPECASM_VERSION 0x8008 +#define SPECASM_VERSION_STR "v8n" #else -#define SPECASM_VERSION 7 -#define SPECASM_VERSION_STR "v7" +#define SPECASM_VERSION 8 +#define SPECASM_VERSION_STR "v8" #endif #include diff --git a/src/state_dump.c b/src/state_dump.c index 70b04dc..ecbea8f 100644 --- a/src/state_dump.c +++ b/src/state_dump.c @@ -60,7 +60,7 @@ void specasm_format_line_e(char *buf, unsigned int l) char *comment_start; char *start; uint8_t i; - const char str_ids[] = {'\'', '"', '#', '@', '-', '+'}; + const char str_ids[] = {'\'', '"', '#', '@', '-', '+', '!'}; const specasm_line_t *line = &state.lines.lines[l]; uint8_t type = specasm_line_get_adj_type(line); @@ -88,7 +88,7 @@ void specasm_format_line_e(char *buf, unsigned int l) goto clear; } if ((type >= SPECASM_LINE_TYPE_STR_SIN_SHORT) && - (type <= SPECASM_LINE_TYPE_INC_SYS_LONG)) { + (type <= SPECASM_LINE_TYPE_INC_BIN_LONG)) { i = (type - SPECASM_LINE_TYPE_STR_SIN_SHORT) >> 1; i = str_ids[i]; buf = prv_format_string_e(buf, line->type & 1, line->data.label, diff --git a/src/state_parse.c b/src/state_parse.c index fe7bcb1..12a66d8 100644 --- a/src/state_parse.c +++ b/src/state_parse.c @@ -280,8 +280,17 @@ static uint8_t prv_parse_include_e(const char *str, char type, uint8_t i, ; l = (k - i) + 1; - line->type = (type == '-') ? SPECASM_LINE_TYPE_INC_SHORT - : SPECASM_LINE_TYPE_INC_SYS_SHORT; + switch (type) { + case '-': + line->type = SPECASM_LINE_TYPE_INC_SHORT; + break; + case '+': + line->type = SPECASM_LINE_TYPE_INC_SYS_SHORT; + break; + default: + line->type = SPECASM_LINE_TYPE_INC_BIN_SHORT; + break; + } memcpy(scratch, &str[i], l); scratch[l] = 0; @@ -407,7 +416,7 @@ void specasm_parse_line_e(unsigned int l, const char *str) if (str[i] == '"' || str[i] == '\'' || str[i] == '@' || str[i] == '#') i = prv_parse_string_e(str, str[i], i + 1, line); - else if (str[i] == '+' || str[i] == '-') + else if (str[i] == '+' || str[i] == '-' || str[i] == '!') i = prv_parse_include_e(str, str[i], i + 1, line); else i = specasm_parse_mnemomic_e(str, i, line); diff --git a/src/test_content.c b/src/test_content.c index dc32ac3..c1870fa 100644 --- a/src/test_content.c +++ b/src/test_content.c @@ -2408,6 +2408,9 @@ const format_test_t format_tests[] = { {"+ sub.x", "+sub.x", SPECASM_LINE_TYPE_INC_SYS_SHORT}, {"+a/very/long/path", "+a/very/long/path", SPECASM_LINE_TYPE_INC_SYS_LONG}, + {"! sub.x", "!sub.x", SPECASM_LINE_TYPE_INC_BIN_SHORT}, + {"!a/very/long/path", "!a/very/long/path", + SPECASM_LINE_TYPE_INC_BIN_LONG}, {".size equ 10 * 10", ".size equ 10 * 10", SPECASM_LINE_TYPE_EQU}, {".size equ 10 * 10", ".size equ 10 * 10", SPECASM_LINE_TYPE_EQU}, {".longlabel1234 equ longlabel1234", ".longlabel1234 equ longlabel1234", @@ -2424,6 +2427,7 @@ const bad_test_t bad_tests[] = { {"addd", SPECASM_ERROR_BAD_MNENOMIC }, {"-", SPECASM_ERROR_BAD_MNENOMIC }, {"+", SPECASM_ERROR_BAD_MNENOMIC }, + {"!", SPECASM_ERROR_BAD_MNENOMIC }, {"$", SPECASM_ERROR_BAD_MNENOMIC }, {"ADD", SPECASM_ERROR_BAD_MNENOMIC }, diff --git a/tests/test_inc_bin/inc_bin.s b/tests/test_inc_bin/inc_bin.s new file mode 100644 index 0000000..452386b --- /dev/null +++ b/tests/test_inc_bin/inc_bin.s @@ -0,0 +1,11 @@ +.Main +db 'A' + jp local +.Global + ret + align 8 +!data +db 'B' +.local +.Global2 + ret diff --git a/tests/test_inc_bin/jump.s b/tests/test_inc_bin/jump.s new file mode 100644 index 0000000..3cc66c8 --- /dev/null +++ b/tests/test_inc_bin/jump.s @@ -0,0 +1,3 @@ + jp Global + jp Global2 + diff --git a/tests/test_inc_bin/test.sh b/tests/test_inc_bin/test.sh new file mode 100755 index 0000000..0020284 --- /dev/null +++ b/tests/test_inc_bin/test.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +set -e +rm inc_bin data 2>/dev/null 1>&2 || true +rm *.x 2>/dev/null 1>&2 || true + +cat /dev/zero | head -c 2049 | tr '\000' '\177' > data + +../../saimport *.s +../../salink 2>/dev/null 1>&2 + +# First alignment byte +byte=`od -An -j5 -tx1 -N1 inc_bin | xargs` +if [ "$byte" != "00" ]; then + echo "Expected 0 byte from alignment" + exit 1 +fi + +# jp local target +word=`od -An -j2 -tx1 -N2 inc_bin | xargs` +if [ "$word" != "0a 88" ]; then + echo "Expected jump target 880a" + exit 1 +fi + +# first file byte +byte=`od -An -j8 -tx1 -N1 inc_bin | xargs` +if [ "$byte" != "7f" ]; then + echo "Expected 7f at byte 8" + exit 1 +fi + +# last file byte +byte=`od -An -j2056 -tx1 -N1 inc_bin | xargs` +if [ "$byte" != "7f" ]; then + echo "Expected 7f at byte 2056" + exit 1 +fi + +# db 'B' +byte=`od -An -j2057 -tx1 -N1 inc_bin | xargs` +if [ "$byte" != "42" ]; then + echo "db 'B'" + exit 1 +fi + +# target of jp Global +word=`od -An -j2060 -tx1 -N2 inc_bin | xargs` +if [ "$word" != "04 80" ]; then + echo "Expected jump target of 8004" + exit 1 +fi + +# target of jp Global2 +word=`od -An -j2063 -tx1 -N2 inc_bin | xargs` +if [ "$word" != "0a 88" ]; then + echo "Expected jump target 880a" + exit 1 +fi + +rm inc_bin +rm data +rm *.x + diff --git a/tests/test_include_dir/01_sub/01.s b/tests/test_include_dir/01_sub/01.s new file mode 100644 index 0000000..0678def --- /dev/null +++ b/tests/test_include_dir/01_sub/01.s @@ -0,0 +1,2 @@ +- 01_sub +db 2 diff --git a/tests/test_include_dir/01_sub/01_sub/01.s b/tests/test_include_dir/01_sub/01_sub/01.s new file mode 100644 index 0000000..3affaf0 --- /dev/null +++ b/tests/test_include_dir/01_sub/01_sub/01.s @@ -0,0 +1 @@ +db 3 diff --git a/tests/test_include_dir/01_sub/02.s b/tests/test_include_dir/01_sub/02.s new file mode 100644 index 0000000..458dd09 --- /dev/null +++ b/tests/test_include_dir/01_sub/02.s @@ -0,0 +1 @@ +db 4 diff --git a/tests/test_include_dir/02_sub/01.s b/tests/test_include_dir/02_sub/01.s new file mode 100644 index 0000000..a89d1db --- /dev/null +++ b/tests/test_include_dir/02_sub/01.s @@ -0,0 +1 @@ +db 5 diff --git a/tests/test_include_dir/02_sub/02.s b/tests/test_include_dir/02_sub/02.s new file mode 100644 index 0000000..677ae00 --- /dev/null +++ b/tests/test_include_dir/02_sub/02.s @@ -0,0 +1 @@ +db 6 diff --git a/tests/test_include_dir/main.s b/tests/test_include_dir/main.s new file mode 100644 index 0000000..73d8bcc --- /dev/null +++ b/tests/test_include_dir/main.s @@ -0,0 +1,4 @@ +- 02_sub +- 01_sub +.Main +db 1 diff --git a/tests/test_include_dir/test.sh b/tests/test_include_dir/test.sh new file mode 100755 index 0000000..1b7750a --- /dev/null +++ b/tests/test_include_dir/test.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e +rm main 2>/dev/null 1>&2 || true +find . -name '*.x' -exec rm {} \; 2>/dev/null 1>&2 || true + + +../../saimport *.s +pushd 01_sub > /dev/null +../../../saimport *.s +pushd 01_sub > /dev/null +../../../../saimport *.s +popd > /dev/null +popd > /dev/null +pushd 02_sub > /dev/null +../../../saimport *.s +popd > /dev/null +../../salink 2>/dev/null 1>&2 + +# First byte should be 1 +byte=`od -An -j0 -tx1 -N1 main | xargs` +if [ "$byte" != "01" ]; then + echo "01 expected got $byte" + exit 1 +fi + +# Second byte should be 2 +byte=`od -An -j1 -tx1 -N1 main | xargs` +if [ "$byte" != "02" ]; then + echo "02 expected got $byte" + exit 1 +fi + +# Third byte should be 3 +byte=`od -An -j2 -tx1 -N1 main | xargs` +if [ "$byte" != "03" ]; then + echo "03 expected got $byte" + exit 1 +fi + +rm main +find . -name '*.x' -exec rm {} \; 2>/dev/null 1>&2 || true + diff --git a/tests/test_include_dir_x/main.s b/tests/test_include_dir_x/main.s new file mode 100644 index 0000000..a87f9c3 --- /dev/null +++ b/tests/test_include_dir_x/main.s @@ -0,0 +1,4 @@ +- sub.x +.Main +db 1 + diff --git a/tests/test_include_dir_x/sub.x/one.s b/tests/test_include_dir_x/sub.x/one.s new file mode 100644 index 0000000..76de642 --- /dev/null +++ b/tests/test_include_dir_x/sub.x/one.s @@ -0,0 +1,2 @@ +db 2 + diff --git a/tests/test_include_dir_x/test.sh b/tests/test_include_dir_x/test.sh new file mode 100755 index 0000000..6b64326 --- /dev/null +++ b/tests/test_include_dir_x/test.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -e +rm main 2>/dev/null 1>&2 || true +find . -name '*.x' -exec rm {} \; 2>/dev/null 1>&2 || true + + +../../saimport *.s +pushd sub.x > /dev/null +../../../saimport *.s +popd > /dev/null +../../salink 2>/dev/null 1>&2 + +# First byte should be 1 +byte=`od -An -j0 -tx1 -N1 main | xargs` +if [ "$byte" != "01" ]; then + echo "01 expected got $byte" + exit 1 +fi + +# Second byte should be 2 +byte=`od -An -j1 -tx1 -N1 main | xargs` +if [ "$byte" != "02" ]; then + echo "02 expected got $byte" + exit 1 +fi + +rm main +find . -name '*.x' -exec rm {} \; 2>/dev/null 1>&2 || true + diff --git a/tests/test_include_missing/main.s b/tests/test_include_missing/main.s new file mode 100644 index 0000000..41ac01e --- /dev/null +++ b/tests/test_include_missing/main.s @@ -0,0 +1,3 @@ +- dontexist +.Main +db 1 diff --git a/tests/test_include_missing/test.sh b/tests/test_include_missing/test.sh new file mode 100755 index 0000000..5490c6c --- /dev/null +++ b/tests/test_include_missing/test.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e +rm *.x 2>/dev/null 1>&2 || true + + +../../saimport *.s +if ../../salink 2>/dev/null 1>&2; then + exit 1 +fi +rm *.x + diff --git a/tests/test_include_self/main.s b/tests/test_include_self/main.s new file mode 100644 index 0000000..ea52721 --- /dev/null +++ b/tests/test_include_self/main.s @@ -0,0 +1,3 @@ +- main.x +.Main + db 1 diff --git a/tests/test_include_self/test.sh b/tests/test_include_self/test.sh new file mode 100755 index 0000000..dc0e653 --- /dev/null +++ b/tests/test_include_self/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +rm equ 2>/dev/null 1>&2 || true +rm *.x 2>/dev/null 1>&2 || true + +../../saimport *.s +if ../../salink 2>/dev/null 1>&2 ; then + exit 1 +fi + +rm *.x + diff --git a/tests/test_include_skip/01_sub/01_sub.x/02.s b/tests/test_include_skip/01_sub/01_sub.x/02.s new file mode 100644 index 0000000..7ff797f --- /dev/null +++ b/tests/test_include_skip/01_sub/01_sub.x/02.s @@ -0,0 +1 @@ +db 2 diff --git a/tests/test_include_skip/01_sub/03.s b/tests/test_include_skip/01_sub/03.s new file mode 100644 index 0000000..3affaf0 --- /dev/null +++ b/tests/test_include_skip/01_sub/03.s @@ -0,0 +1 @@ +db 3 diff --git a/tests/test_include_skip/main.s b/tests/test_include_skip/main.s new file mode 100644 index 0000000..9f7e101 --- /dev/null +++ b/tests/test_include_skip/main.s @@ -0,0 +1,6 @@ +; This should just include +; 01_sub/*.x. It should not +; include 01_sub/01_sub.x/*.x. +- 01_sub +.Main +db 1 diff --git a/tests/test_include_skip/test.sh b/tests/test_include_skip/test.sh new file mode 100755 index 0000000..93384e6 --- /dev/null +++ b/tests/test_include_skip/test.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e +rm main 2>/dev/null 1>&2 || true +find . -name '*.x' -exec rm {} \; 2>/dev/null 1>&2 || true + + +../../saimport *.s +pushd 01_sub > /dev/null +../../../saimport *.s +pushd 01_sub.x > /dev/null +../../../../saimport *.s +popd > /dev/null +popd > /dev/null +../../salink 2>/dev/null 1>&2 + +# First byte should be 1 +byte=`od -An -j0 -tx1 -N1 main | xargs` +if [ "$byte" != "01" ]; then + echo "Main object not first" + exit 1 +fi + +# Second byte should be 3 +byte=`od -An -j1 -tx1 -N1 main | xargs` +if [ "$byte" != "03" ]; then + echo "Expected 03 got $byte" + exit 1 +fi + +rm main +find . -name '*.x' -exec rm {} \; 2>/dev/null 1>&2 || true + diff --git a/tests/test_include_twice/main.s b/tests/test_include_twice/main.s new file mode 100644 index 0000000..77caba8 --- /dev/null +++ b/tests/test_include_twice/main.s @@ -0,0 +1,3 @@ +- one.x +.Main + db 1 diff --git a/tests/test_include_twice/one.s b/tests/test_include_twice/one.s new file mode 100644 index 0000000..7ff797f --- /dev/null +++ b/tests/test_include_twice/one.s @@ -0,0 +1 @@ +db 2 diff --git a/tests/test_include_twice/test.sh b/tests/test_include_twice/test.sh new file mode 100755 index 0000000..f49a34c --- /dev/null +++ b/tests/test_include_twice/test.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e +rm equ 2>/dev/null 1>&2 || true +rm *.x 2>/dev/null 1>&2 || true + +../../saimport *.s +if ../../salink 2>/dev/null 1>&2 ; then + exit 1 +fi + +rm *.x diff --git a/tests/test_link_order/00_data.s b/tests/test_link_order/00_data.s new file mode 100644 index 0000000..7ff797f --- /dev/null +++ b/tests/test_link_order/00_data.s @@ -0,0 +1 @@ +db 2 diff --git a/tests/test_link_order/01_data.s b/tests/test_link_order/01_data.s new file mode 100644 index 0000000..3affaf0 --- /dev/null +++ b/tests/test_link_order/01_data.s @@ -0,0 +1 @@ +db 3 diff --git a/tests/test_link_order/main.s b/tests/test_link_order/main.s new file mode 100644 index 0000000..3746fa5 --- /dev/null +++ b/tests/test_link_order/main.s @@ -0,0 +1,2 @@ +.Main +db 1 diff --git a/tests/test_link_order/test.sh b/tests/test_link_order/test.sh new file mode 100755 index 0000000..b4f7227 --- /dev/null +++ b/tests/test_link_order/test.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e +rm main 2>/dev/null 1>&2 || true +rm *.x 2>/dev/null 1>&2 || true + +../../saimport *.s +../../salink 2>/dev/null 1>&2 + +# First byte should be 1 +byte=`od -An -j0 -tx1 -N1 main | xargs` +if [ "$byte" != "01" ]; then + echo "Main object not first" + exit 1 +fi + +# Second byte should be 1 +byte=`od -An -j1 -tx1 -N1 main | xargs` +if [ "$byte" != "02" ]; then + echo "00_data.s is not second" + exit 1 +fi + +# Third byte should be 2 +byte=`od -An -j2 -tx1 -N1 main | xargs` +if [ "$byte" != "03" ]; then + echo "01_data.s is not third" + exit 1 +fi + +rm main +rm *.x +