Skip to content

Commit bf82df3

Browse files
committed
Load programs from external flash files
1 parent 3716c9b commit bf82df3

File tree

4 files changed

+278
-210
lines changed

4 files changed

+278
-210
lines changed

src/displayapp/screens/Pawn.cpp

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66

77
using namespace Pinetime::Applications::Screens;
88

9+
#define LOG_PREFIX "[Pawn] "
10+
911
enum {
1012
PAWN_ERR_PARAMCOUNT = 100,
1113
PAWN_ERR_MISSINGHANDLER,
1214
PAWN_ERR_INVALIDSTRING,
1315
PAWN_ERR_INVALIDSETTING,
1416
PAWN_ERR_INVALIDTRAMPOLINE,
17+
PAWN_ERR_FILE,
1518

1619
PAWN_ERR_FIRST = PAWN_ERR_PARAMCOUNT,
1720
};
@@ -26,7 +29,7 @@ enum {
2629

2730
#define PAWN_INST ((Pawn*) amx->userdata[0])
2831

29-
constexpr int max_overlay_size = 512;
32+
constexpr int max_overlay_size = 2048;
3033

3134
static void label_set_text(AMX* amx, lv_obj_t* label, cell str) {
3235
char* text;
@@ -464,10 +467,13 @@ static int AMXAPI prun_Overlay(AMX* amx, int index) {
464467
amx->code = (unsigned char*) amx_poolfind(&PAWN_INST->amx_pool, index);
465468

466469
if (amx->code == NULL) {
470+
NRF_LOG_INFO(LOG_PREFIX "Reading overlay %d, %d bytes", index, tbl->size);
471+
467472
if ((amx->code = (unsigned char*) amx_poolalloc(&PAWN_INST->amx_pool, tbl->size, index)) == NULL)
468473
return AMX_ERR_OVERLAY;
469474

470-
memcpy(amx->code, PAWN_INST->file + hdr->cod + tbl->offset, tbl->size);
475+
if (PAWN_INST->file->Read(amx->code, tbl->size, hdr->cod + tbl->offset) < (size_t) tbl->size)
476+
return PAWN_ERR_FILE;
471477
}
472478

473479
return AMX_ERR_NONE;
@@ -488,70 +494,81 @@ static int AMXAPI prun_Callback(struct tagAMX* amx, cell index, cell* result, co
488494
}
489495

490496
int Pawn::LoadProgram() {
497+
NRF_LOG_INFO(LOG_PREFIX "Loading program");
498+
491499
int result;
492500
AMX_HEADER hdr;
493-
memcpy(&hdr, file, sizeof(hdr));
501+
if (file->Read((uint8_t*) &hdr, sizeof(hdr), 0) < sizeof(hdr))
502+
return PAWN_ERR_FILE;
494503

495504
if (hdr.magic != AMX_MAGIC)
496505
return AMX_ERR_FORMAT;
497506

498507
memset(&amx, 0, sizeof(amx));
499508
amx.userdata[0] = this;
500509

501-
header = malloc(hdr.cod);
510+
header = std::make_unique<uint8_t[]>(hdr.cod);
502511
if (header == NULL)
503512
return AMX_ERR_MEMORY;
504513

505-
memcpy(header, file, hdr.cod);
514+
if (file->Read((uint8_t*) header.get(), hdr.cod, 0) < (size_t) hdr.cod)
515+
return PAWN_ERR_FILE;
506516

507-
datablock = malloc(hdr.stp - hdr.dat); // This block contains data, heap and stack
517+
datablock = std::make_unique<uint8_t[]>(hdr.stp - hdr.dat); // This block contains data, heap and stack
508518
if (datablock == NULL)
509519
return AMX_ERR_MEMORY;
510520

511-
memcpy(datablock, file + hdr.dat, hdr.hea - hdr.dat);
512-
amx.data = (unsigned char*) datablock;
521+
if (file->Read((uint8_t*) datablock.get(), hdr.hea - hdr.dat, hdr.dat) < (size_t) hdr.hea - hdr.dat)
522+
return PAWN_ERR_FILE;
523+
524+
amx.data = (unsigned char*) datablock.get();
513525

514526
if (hdr.flags & AMX_FLAG_OVERLAY) {
527+
NRF_LOG_INFO(LOG_PREFIX "Program has overlays");
528+
515529
constexpr int overlaypool_overhead = 8;
516-
overlaypool = malloc(max_overlay_size + overlaypool_overhead);
530+
overlaypool = std::make_unique<uint8_t[]>(max_overlay_size + overlaypool_overhead);
517531
if (overlaypool == NULL)
518532
return AMX_ERR_MEMORY;
519533

520-
amx_poolinit(&amx_pool, overlaypool, max_overlay_size + overlaypool_overhead);
534+
amx_poolinit(&amx_pool, overlaypool.get(), max_overlay_size + overlaypool_overhead);
521535

522536
amx.overlay = prun_Overlay;
523537

524-
result = amx_Init(&amx, header);
525-
if (result != AMX_ERR_NONE) {
526-
free(header);
527-
header = NULL;
528-
free(datablock);
529-
datablock = NULL;
530-
free(overlaypool);
531-
overlaypool = NULL;
532-
}
538+
result = amx_Init(&amx, header.get());
533539
} else {
540+
NRF_LOG_INFO(LOG_PREFIX "Program doesn't have overlays");
541+
534542
amx.flags |= AMX_FLAG_DSEG_INIT;
535543

536-
result = amx_Init(&amx, (void*) file);
537-
if (result != AMX_ERR_NONE) {
538-
free(header);
539-
header = NULL;
540-
free(datablock);
541-
datablock = NULL;
544+
// Happy path: the file is backed by a const array and we can reference it directly
545+
const uint8_t* code = file->GetConst();
546+
547+
if (code == nullptr || true) {
548+
// Slow path: we must read the whole file into memory
549+
NRF_LOG_INFO(LOG_PREFIX "Loading program into RAM");
550+
551+
filecode = std::make_unique<uint8_t[]>(hdr.size);
552+
if (file->Read(filecode.get(), hdr.size, 0) < (size_t) hdr.size)
553+
return PAWN_ERR_FILE;
554+
555+
code = filecode.get();
556+
} else {
557+
NRF_LOG_INFO(LOG_PREFIX "Loaded program from constant")
542558
}
559+
560+
result = amx_Init(&amx, (void*) code);
543561
}
544562

545563
return result;
546564
}
547565

548566
#include "program.h"
549567

550-
Pawn::Pawn(AppControllers& controllers) : Pawn(program, controllers) {
551-
(void) program_len;
568+
Pawn::Pawn(AppControllers& controllers) : Pawn(controllers, std::make_unique<LfsFile>(controllers.filesystem, "/flash.amx")) {
552569
}
553570

554-
Pawn::Pawn(const uint8_t* file, AppControllers& controllers) : controllers(controllers), file(file) {
571+
Pawn::Pawn(AppControllers& controllers, std::unique_ptr<File> file) : controllers(controllers), file(std::move(file)) {
555572
int result = LoadProgram();
556573
if (result != AMX_ERR_NONE) {
557574
ShowError(result);
@@ -604,13 +621,6 @@ Pawn::~Pawn() {
604621
CleanUI();
605622

606623
amx_Cleanup(&amx);
607-
608-
if (header)
609-
free(header);
610-
if (datablock)
611-
free(datablock);
612-
if (overlaypool)
613-
free(overlaypool);
614624
}
615625

616626
void Pawn::Refresh() {
@@ -631,6 +641,8 @@ void Pawn::ShowError(unsigned int amx_err) {
631641
"missing event handler", // PAWN_ERR_MISSINGHANDLER
632642
"invalid string", // PAWN_ERR_INVALIDSTRING
633643
"invalid setting", // PAWN_ERR_INVALIDSETTING
644+
"invalid trampoline", // PAWN_ERR_INVALIDTRAMPOLINE
645+
"file read error", // PAWN_ERR_FILE
634646
};
635647

636648
if (amx_err == AMX_ERR_EXIT) {

src/displayapp/screens/Pawn.h

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,62 @@ namespace Pinetime {
1717

1818
class Pawn : public Screen {
1919
public:
20+
struct File {
21+
virtual ~File() = default;
22+
23+
virtual const uint8_t* GetConst() {
24+
return nullptr;
25+
}
26+
27+
virtual size_t Read(uint8_t* buffer, size_t size, size_t offset) = 0;
28+
};
29+
30+
class ConstFile : public File {
31+
const uint8_t* backing;
32+
size_t size;
33+
34+
public:
35+
ConstFile(const uint8_t* backing, size_t size) : backing(backing), size(size) {
36+
}
37+
38+
const uint8_t* GetConst() override {
39+
return backing;
40+
}
41+
42+
size_t Read(uint8_t* buffer, size_t size, size_t offset) override {
43+
if (size + offset > this->size)
44+
return 0;
45+
memcpy(buffer, backing + offset, size);
46+
return size;
47+
}
48+
};
49+
50+
class LfsFile : public File {
51+
Controllers::FS& fs;
52+
lfs_file_t file;
53+
bool ok;
54+
55+
public:
56+
LfsFile(Controllers::FS& fs, const char* path) : fs(fs) {
57+
ok = fs.FileOpen(&file, path, LFS_O_RDONLY) == LFS_ERR_OK;
58+
}
59+
60+
~LfsFile() override {
61+
if (ok)
62+
fs.FileClose(&file);
63+
}
64+
65+
size_t Read(uint8_t* buffer, size_t size, size_t offset) override {
66+
if (!ok)
67+
return 0;
68+
69+
fs.FileSeek(&file, offset);
70+
return fs.FileRead(&file, buffer, size);
71+
}
72+
};
73+
2074
Pawn(AppControllers& controllers);
21-
Pawn(const uint8_t *file, AppControllers& controllers);
75+
Pawn(AppControllers& controllers, std::unique_ptr<File> file);
2276
~Pawn() override;
2377

2478
void Refresh() override;
@@ -36,7 +90,7 @@ namespace Pinetime {
3690
Widgets::StatusIcons* statusIcons = nullptr;
3791

3892
amxPool amx_pool;
39-
const uint8_t *file;
93+
std::unique_ptr<File> file;
4094

4195
private:
4296
AMX amx;
@@ -45,7 +99,7 @@ namespace Pinetime {
4599
lv_task_t* taskRefresh = 0;
46100
unsigned int queued_error = 0;
47101

48-
void *header = nullptr, *datablock = nullptr, *overlaypool = nullptr;
102+
std::unique_ptr<uint8_t[]> header, datablock, overlaypool, filecode;
49103

50104
int LoadProgram();
51105
void CleanUI();

src/displayapp/screens/WatchFaceDigital.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55

66
using namespace Pinetime::Applications::Screens;
77

8-
WatchFaceDigital::WatchFaceDigital(AppControllers& controllers) : Pawn(watchface_digital, controllers) {
8+
WatchFaceDigital::WatchFaceDigital(AppControllers& controllers) : Pawn(controllers, std::make_unique<Pawn::ConstFile>(watchface_digital, watchface_digital_len)) {
99
}

0 commit comments

Comments
 (0)