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
Add support for the '!' directive
Which allows files to be included at specific locations in
the final binary generated by salink.

Signed-off-by: Mark Ryan <[email protected]>
  • Loading branch information
markdryan committed Feb 16, 2024
commit 65fc3aa2d29e10da45b27e7c431c61aeaa755377
29 changes: 29 additions & 0 deletions docs/specasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,35 @@ Here are some examples of their use
-lib/math.x
```

### 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> | 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

Salink generates a raw binary file. Before the binary can be executed, memory needs to be reserved, the binary needs to be loaded into memory at the address at which it was assembled and, finally, it needs to be inovked. Assuming that the binary was assembled to the default address, i.e., 32768, and is called "bin", this can all be achieved in BASIC as follows.
Expand Down
2 changes: 2 additions & 0 deletions src/line.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
124 changes: 116 additions & 8 deletions src/link_obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,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)
{
Expand All @@ -171,12 +234,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) {
Expand All @@ -194,11 +255,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);
Expand Down Expand Up @@ -291,7 +351,6 @@ static uint16_t prv_align_e(specasm_handle_t f, uint16_t align)
return adjust;
}


static uint8_t prv_add_label_e(salink_obj_t *obj, uint16_t size, uint8_t type,
uint8_t id, uint16_t line_no)
{
Expand Down Expand Up @@ -493,6 +552,48 @@ 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_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;

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;

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)
{
uint16_t i;
Expand Down Expand Up @@ -555,6 +656,9 @@ static void prv_parse_obj_e(const char *fname)
} 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);
} 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);
}
Expand Down Expand Up @@ -737,6 +841,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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/state_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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,
Expand Down
15 changes: 12 additions & 3 deletions src/state_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/test_content.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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 },

Expand Down
11 changes: 11 additions & 0 deletions tests/test_inc_bin/inc_bin.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.Main
db 'A'
jp local
.Global
ret
align 8
!data
db 'B'
.local
.Global2
ret
3 changes: 3 additions & 0 deletions tests/test_inc_bin/jump.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
jp Global
jp Global2

64 changes: 64 additions & 0 deletions tests/test_inc_bin/test.sh
Original file line number Diff line number Diff line change
@@ -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