Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions docs/assembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ variable declared with `.var`; built-in variables are read-only.
| 0x48 | `CLAMP` | `$d, lo, hi` | d = min(max(d, lo), hi) |
| 0x49 | `TOGGLE` | `$d` | d = (d == 0) ? 1 : 0 |
| 0x4A | `FORMAT` | `$d, str, n` | Format n into string variable d: `str` with one `%d` or `%x` - optionally with a width, padded with spaces or, after a `0`, with zeros (`%02d`, `%4x`) - and `%%` for `%` |
| 0x4B | `APPEND` | `$d, str` | Append `str` (a literal or a string variable) to string variable d, as much of it as d's size takes - text put together from pieces (format 0.5) |

`str` can be a string variable, so a format can be made by `FORMAT` itself -
a clock from minutes:
Expand All @@ -502,6 +503,15 @@ a clock from minutes:
FORMAT $clock, $text, $n ; 12:05
```

Text from several pieces - a number and a unit, a name and a value - is
`FORMAT` or `SET` followed by `APPEND`s:

```
FORMAT $speed, "%d", $kmh ; "68"
APPEND $speed, " "
APPEND $speed, $unit ; "68 km/h"
```

A variable is "changed" only when its value differs from the previous one -
`SET $v, 5` on a `$v` that already holds 5 invalidates nothing.

Expand Down
6 changes: 3 additions & 3 deletions docs/binary-format.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# dmview Binary Format (`.dmv`)

Status: **version 0.4** - follows the [assembly draft](assembly.md). Version
0.2 added gradients, 0.3 the `OPACITY` instruction, 0.4 `ICON`.
Status: **version 0.5** - follows the [assembly draft](assembly.md). Version
0.2 added gradients, 0.3 the `OPACITY` instruction, 0.4 `ICON`, 0.5 `APPEND`.

A `.dmv` file is what `libtodmv` produces from `.dmvs` assembly and what
`libdmview` executes. Everything is **little-endian**; every table starts at a
Expand Down Expand Up @@ -57,7 +57,7 @@ start of the file. An empty table has count 0.

A version 0.1 header ends at 80, without the gradient tables. `libtodmv`
always writes the 96-byte header, but marks a view with the oldest version
that has what it uses - 0.4 with `ICON`, 0.3 with `OPACITY`, 0.2 with gradients, else 0.1: a
that has what it uses - 0.5 with `APPEND`, 0.4 with `ICON`, 0.3 with `OPACITY`, 0.2 with gradients, else 0.1: a
reader that knows only 0.1 runs a 0.1 view, it finds every table through
the header and never looks at bytes 80 ... 95.

Expand Down
6 changes: 4 additions & 2 deletions include/dmview_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@

/** Format version this header describes. */
#define DMV_VERSION_MAJOR 0
#define DMV_VERSION_MINOR 4
#define DMV_VERSION_MINOR 5

/** Header size: version 0.2 added the gradient tables at its end (0.3:
* OPACITY, 0.4: ICON - no change to the header). */
* OPACITY, 0.4: ICON, 0.5: APPEND - no change to the header). */
#define DMV_HEADER_SIZE 96u
#define DMV_HEADER_SIZE_0_1 80u

Expand Down Expand Up @@ -106,6 +106,7 @@ typedef enum
DMV_OP_CLAMP = 0x48,
DMV_OP_TOGGLE = 0x49,
DMV_OP_FORMAT = 0x4A,
DMV_OP_APPEND = 0x4B,

/* Input */
DMV_OP_ON = 0x60,
Expand Down Expand Up @@ -607,6 +608,7 @@ static const dmv_opcode_info_t dmv_opcode_table[DMV_OPCODE_TABLE_SIZE] = {
OP(DMV_OP_CLAMP, "CLAMP", VARIA, 3, 0, F_NONE, false, VAR, V32, V32),
OP(DMV_OP_TOGGLE, "TOGGLE", VARIA, 1, 0, F_NONE, false, VAR),
OP(DMV_OP_FORMAT, "FORMAT", VARIA, 3, 0, F_NONE, false, VAR, STR, V32),
OP(DMV_OP_APPEND, "APPEND", VARIA, 2, 0, F_NONE, false, VAR, STR),

OP(DMV_OP_ON, "ON", INPUT, 2, 0, F_NONE, false, EVENT, LABEL),

Expand Down
7 changes: 7 additions & 0 deletions src/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,13 @@ static void run(struct libdmview* v, uint32_t pc, bool box_region)
view_set_string(v, rd16(insn + OPOFF(FORMAT, 0)), text);
break;
}
case DMV_OP_APPEND:
{
uint16_t d = rd16(insn + OPOFF(APPEND, 0));
record(v, d / 32U, 1U << (d % 32U));
view_append_string(v, d, vstr(v, insn, 1, OPOFF(APPEND, 1)));
break;
}

/* ---- Input ---- */
case DMV_OP_ON:
Expand Down
1 change: 1 addition & 0 deletions src/layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ static inline uint8_t layout_offset(uint8_t op, unsigned i)
case DMV_OP_CLAMP: { static const uint8_t o[] = { 4, 8, 12 }; return o[i]; }
case DMV_OP_TOGGLE: { static const uint8_t o[] = { 4 }; return o[i]; }
case DMV_OP_FORMAT: { static const uint8_t o[] = { 4, 6, 8 }; return o[i]; }
case DMV_OP_APPEND: { static const uint8_t o[] = { 4, 6 }; return o[i]; }
case DMV_OP_ON: { static const uint8_t o[] = { 4, 6 }; return o[i]; }
case DMV_OP_REDRAW:
case DMV_OP_EXEC:
Expand Down
1 change: 1 addition & 0 deletions src/private.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ void draw_icon(const libdmview_surface_t* s, const rect_t* clip, int3
const char* view_string(const struct libdmview* v, uint32_t index);
void view_set_int(struct libdmview* v, uint32_t index, int32_t value);
void view_set_string(struct libdmview* v, uint32_t index, const char* value);
void view_append_string(struct libdmview* v, uint32_t index, const char* tail);
void view_invalidate_deps(struct libdmview* v, uint32_t word, uint32_t bit);
void view_mark_dirty(struct libdmview* v, int32_t box);

Expand Down
1 change: 1 addition & 0 deletions src/validate.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ static int dest_type(uint8_t opcode)
{
case DMV_OP_SET: return -1; /* Either */
case DMV_OP_FORMAT: return DMV_VAR_STR;
case DMV_OP_APPEND: return DMV_VAR_STR;
default: return DMV_VAR_INT;
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/view.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,30 @@ void view_set_string(struct libdmview* v, uint32_t index, const char* value)
(void)Dmod_SetEnv(view_string(v, v->vars[index].env), dst, 1);
}

/* The string after what the variable holds - as much of it as its size takes */
void view_append_string(struct libdmview* v, uint32_t index, const char* tail)
{
char* dst = v->strs[index];
size_t n = strlen(dst), room = v->vars[index].capacity;
size_t m = strlen(tail);
if (n >= room || m == 0)
return;
if (m > room - n)
m = room - n;
if (tail >= dst && tail <= dst + room)
{
/* Appended to itself: copy it from before it grows */
memmove(dst + n, tail, m);
}
else
memcpy(dst + n, tail, m);
dst[n + m] = '\0';
if (!v->drawing)
view_invalidate_deps(v, index / 32U, 1U << (index % 32U));
if ((v->vars[index].flags & DMV_VARF_ENV) != 0)
(void)Dmod_SetEnv(view_string(v, v->vars[index].env), dst, 1);
}

/* ---- Loading ---- */

static void free_view(struct libdmview* v)
Expand Down
27 changes: 27 additions & 0 deletions tests/fixtures/append.dmvs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
; APPEND: text put together from pieces - every line is drawn above the same
; text as a literal, in the built-in font, on a 64x32 surface
.view append
.size 64, 32
.entry draw

.font fixed, "builtin-8"

.var $a, str[12], ""
.var $b, str[4], ""
.var $t, str[8], "km"
.var $n, str[8], ""

draw:
FILL #000000
FORMAT $n, "%d", 68
SET $a, $n
APPEND $a, " "
APPEND $a, $t
APPEND $a, "/h"
TEXT 0, 0, 64, 8, $a, fixed, #FFFFFF, LEFT ; "68 km/h"
TEXT 0, 8, 64, 8, "68 km/h", fixed, #FFFFFF, LEFT
SET $b, "ab"
APPEND $b, "cdef"
TEXT 0, 16, 64, 8, $b, fixed, #FFFFFF, LEFT ; "abcd": its size
TEXT 0, 24, 64, 8, "abcd", fixed, #FFFFFF, LEFT
RET
10 changes: 10 additions & 0 deletions tests/libdmview_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,16 @@ DMOD_TEST_STEP(libdmview_formats_with_padding)
DMOD_TEST_EXPECT_EQ(libdmview_get_size(NULL, &w, &h), -EINVAL);
}

DMOD_TEST_STEP(libdmview_appends_strings)
{
DMOD_TEST_EXPECT_TRUE(open_fixture(FIXTURE("append.dmv")));
if (g_view == NULL)
return;
DMOD_TEST_EXPECT_EQ(libdmview_render(g_view, &g_s32, NULL), 1);
DMOD_TEST_EXPECT_TRUE(same_as_below(0, 64, 0)); /* "68" " " "km" "/h": "68 km/h" */
DMOD_TEST_EXPECT_TRUE(same_as_below(0, 64, 16)); /* "ab" + "cdef" into str[4]: "abcd" */
}

/* ---- Antialiasing ---- */


Expand Down
Loading