Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Allow '-' and '+' to include directories
In addition to be able to add individual .x files to the binary,
'-' and '+' can now be used to include all the .x files in a
given directory.  This makes it much easier to split the program
up into separate directories.  Directory inclusion is not recursive.
If the included directory contains a sub-directory with .x files
these files will be ignored by the linker unless that subdirectory
is also explicitly included.

Signed-off-by: Mark Ryan <[email protected]>
  • Loading branch information
markdryan committed Feb 25, 2024
commit a5f8f1297f41861110254a77b01f7788a7345a80
33 changes: 31 additions & 2 deletions docs/specasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,10 @@ will cause the linked program to be assembled at 23760.

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.

Expand All @@ -645,16 +648,42 @@ 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 <filename> 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
+gr/rows

; 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.
Expand Down
192 changes: 164 additions & 28 deletions src/link_obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -451,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;
Expand All @@ -470,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] != '/') {
Expand All @@ -487,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);
Expand All @@ -498,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) {
Expand All @@ -510,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)
{
Expand Down Expand Up @@ -555,26 +667,21 @@ static void prv_add_equ_label_e(specasm_line_t *line, salink_obj_t *obj,
static uint16_t prv_inc_bin_size_e(specasm_line_t *line, uint16_t size)
{
const char *fname;
specasm_handle_t in_f;
specasm_stat_t stat_buf;
uint32_t bin_size;
uint32_t new_size;
specasm_error_t err;

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)
goto cant_open;

specasm_file_stat_e(in_f, &stat_buf);
err = err_type;
specasm_file_close_e(in_f);
err_type = err;
if (err_type != SPECASM_ERROR_OK)
goto cant_open;
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;
Expand All @@ -586,12 +693,6 @@ static uint16_t prv_inc_bin_size_e(specasm_line_t *line, uint16_t size)
}

return (uint16_t)new_size;

cant_open:
snprintf(error_buf, sizeof(error_buf), "Can't open or stat %s", fname);
err_type = SALINK_ERROR_CANT_OPEN;

return 0;
}

static void prv_parse_obj_e(const char *fname)
Expand Down Expand Up @@ -619,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;
Expand Down Expand Up @@ -655,7 +756,7 @@ 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);
Expand Down Expand Up @@ -730,6 +831,34 @@ static uint8_t prv_order_objects_e(void)
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)
{
unsigned int i;
Expand Down Expand Up @@ -926,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) {
Expand All @@ -934,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));
Expand All @@ -960,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;
Expand Down
8 changes: 6 additions & 2 deletions src/peer_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <arch/zxn/esxdos.h>
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
Expand All @@ -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);
Expand All @@ -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
Loading