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
salink: link .x files in ascending order of their names
It is now possible for the user to control the order in which the
code in the various .x files that make up a Specasm project gets
written into the final binary.  The file with the .Main label
always comes first followed by the code in the remainining files
which are written in ascending order of their filenames.  This
feature is intended for programatically generated .x files like
compilers, which might generate more code that can fit into a
single .x file.

Signed-off-by: Mark Ryan <[email protected]>
  • Loading branch information
markdryan committed Feb 16, 2024
commit cb59e3674777c0a4da88c0f63e09e383059578a9
14 changes: 11 additions & 3 deletions docs/specasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -581,15 +581,23 @@ 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
```

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
Expand Down Expand Up @@ -624,7 +632,7 @@ 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

Expand Down
48 changes: 26 additions & 22 deletions src/link_obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -689,16 +689,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) {
Expand All @@ -707,23 +714,20 @@ 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_complete_absolutes_e(void)
Expand All @@ -738,7 +742,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];

Expand Down Expand Up @@ -869,13 +873,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)
Expand Down
1 change: 1 addition & 0 deletions src/salink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/salink.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,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
1 change: 1 addition & 0 deletions tests/test_link_order/00_data.s
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
db 2
1 change: 1 addition & 0 deletions tests/test_link_order/01_data.s
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
db 3
2 changes: 2 additions & 0 deletions tests/test_link_order/main.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.Main
db 1
33 changes: 33 additions & 0 deletions tests/test_link_order/test.sh
Original file line number Diff line number Diff line change
@@ -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