mirror of
https://github.com/InfiniTimeOrg/InfiniTime.git
synced 2024-10-22 15:11:51 +02:00
Compare commits
38 commits
52061ad50f
...
071361b9b9
Author | SHA1 | Date | |
---|---|---|---|
071361b9b9 | |||
8598142c27 | |||
21c3b1ebb0 | |||
c59e29a5c8 | |||
23b1bfcb9a | |||
86c8fd882d | |||
47522994f5 | |||
700e9a4f6d | |||
9bc6c820e9 | |||
ff4607c6da | |||
b55b59ea13 | |||
b7cdd48157 | |||
06fffa6df2 | |||
c5183c0cba | |||
9bc75c5d3c | |||
3556687e2e | |||
46c25e83bc | |||
c9f22468ce | |||
d6b51579e5 | |||
408f4bd6dc | |||
d3706722e0 | |||
1751c4d5b4 | |||
db53157c21 | |||
2adeb39800 | |||
562035dcab | |||
e9db08e589 | |||
29f1a93839 | |||
bf13db05ce | |||
7ac66dc237 | |||
23729eff99 | |||
8d5ac4f4c2 | |||
0faece9e4b | |||
e677d1cb42 | |||
c7d58fc97a | |||
350567a6d2 | |||
f173f1b1d5 | |||
c4bea51725 | |||
b6f4234517 |
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -4,9 +4,6 @@
|
||||||
[submodule "src/libs/littlefs"]
|
[submodule "src/libs/littlefs"]
|
||||||
path = src/libs/littlefs
|
path = src/libs/littlefs
|
||||||
url = https://github.com/littlefs-project/littlefs.git
|
url = https://github.com/littlefs-project/littlefs.git
|
||||||
[submodule "src/libs/QCBOR"]
|
|
||||||
path = src/libs/QCBOR
|
|
||||||
url = https://github.com/laurencelundblade/QCBOR.git
|
|
||||||
[submodule "src/libs/arduinoFFT"]
|
[submodule "src/libs/arduinoFFT"]
|
||||||
path = src/libs/arduinoFFT
|
path = src/libs/arduinoFFT
|
||||||
url = https://github.com/kosme/arduinoFFT.git
|
url = https://github.com/kosme/arduinoFFT.git
|
||||||
|
|
|
@ -397,6 +397,7 @@ list(APPEND SOURCE_FILES
|
||||||
displayapp/screens/Alarm.cpp
|
displayapp/screens/Alarm.cpp
|
||||||
displayapp/screens/Styles.cpp
|
displayapp/screens/Styles.cpp
|
||||||
displayapp/screens/WeatherSymbols.cpp
|
displayapp/screens/WeatherSymbols.cpp
|
||||||
|
displayapp/screens/ASM.cpp
|
||||||
displayapp/Colors.cpp
|
displayapp/Colors.cpp
|
||||||
displayapp/widgets/Counter.cpp
|
displayapp/widgets/Counter.cpp
|
||||||
displayapp/widgets/PageIndicator.cpp
|
displayapp/widgets/PageIndicator.cpp
|
||||||
|
|
|
@ -70,7 +70,11 @@ int FS::FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
|
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
|
||||||
return lfs_file_seek(&lfs, file_p, pos, LFS_SEEK_SET);
|
return FileSeek(file_p, pos, LFS_SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos, int whence) {
|
||||||
|
return lfs_file_seek(&lfs, file_p, pos, whence);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FS::FileDelete(const char* fileName) {
|
int FS::FileDelete(const char* fileName) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace Pinetime {
|
||||||
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
|
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
|
||||||
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
|
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
|
||||||
int FileSeek(lfs_file_t* file_p, uint32_t pos);
|
int FileSeek(lfs_file_t* file_p, uint32_t pos);
|
||||||
|
int FileSeek(lfs_file_t* file_p, uint32_t pos, int whence);
|
||||||
|
|
||||||
int FileDelete(const char* fileName);
|
int FileDelete(const char* fileName);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "displayapp/apps/Apps.h"
|
#include "displayapp/apps/Apps.h"
|
||||||
#include "Controllers.h"
|
#include "Controllers.h"
|
||||||
|
|
||||||
|
#include "displayapp/screens/ASM.h"
|
||||||
#include "displayapp/screens/Alarm.h"
|
#include "displayapp/screens/Alarm.h"
|
||||||
#include "displayapp/screens/Dice.h"
|
#include "displayapp/screens/Dice.h"
|
||||||
#include "displayapp/screens/Timer.h"
|
#include "displayapp/screens/Timer.h"
|
||||||
|
|
|
@ -42,7 +42,8 @@ namespace Pinetime {
|
||||||
SettingChimes,
|
SettingChimes,
|
||||||
SettingShakeThreshold,
|
SettingShakeThreshold,
|
||||||
SettingBluetooth,
|
SettingBluetooth,
|
||||||
Error
|
Error,
|
||||||
|
ASM
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class WatchFace : uint8_t {
|
enum class WatchFace : uint8_t {
|
||||||
|
|
|
@ -2,6 +2,7 @@ if(DEFINED ENABLE_USERAPPS)
|
||||||
set(USERAPP_TYPES ${ENABLE_USERAPPS} CACHE STRING "List of user apps to build into the firmware")
|
set(USERAPP_TYPES ${ENABLE_USERAPPS} CACHE STRING "List of user apps to build into the firmware")
|
||||||
else ()
|
else ()
|
||||||
set(DEFAULT_USER_APP_TYPES "Apps::StopWatch")
|
set(DEFAULT_USER_APP_TYPES "Apps::StopWatch")
|
||||||
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::ASM")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Alarm")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Alarm")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Timer")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Timer")
|
||||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Steps")
|
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Steps")
|
||||||
|
|
564
src/displayapp/screens/ASM.cpp
Normal file
564
src/displayapp/screens/ASM.cpp
Normal file
|
@ -0,0 +1,564 @@
|
||||||
|
#include "ASM.h"
|
||||||
|
|
||||||
|
#include <libraries/log/nrf_log.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
|
constexpr lv_font_t* fonts[] = {
|
||||||
|
&fontawesome_weathericons,
|
||||||
|
&jetbrains_mono_42,
|
||||||
|
&jetbrains_mono_76,
|
||||||
|
&jetbrains_mono_bold_20,
|
||||||
|
&jetbrains_mono_extrabold_compressed,
|
||||||
|
&lv_font_sys_48,
|
||||||
|
&open_sans_light,
|
||||||
|
};
|
||||||
|
constexpr int num_fonts = sizeof(fonts) / sizeof(fonts[0]);
|
||||||
|
|
||||||
|
constexpr uint32_t handler_return_pc_mark = 1 << 31;
|
||||||
|
|
||||||
|
struct CallbackInfo {
|
||||||
|
ASM* instance;
|
||||||
|
uint32_t callback_pc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void event_handler(lv_obj_t* obj, lv_event_t event) {
|
||||||
|
CallbackInfo* cbInfo = static_cast<CallbackInfo*>(lv_obj_get_user_data(obj));
|
||||||
|
cbInfo->instance->OnObjectEvent(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASM::ASM(Controllers::DateTime& dateTimeController,
|
||||||
|
const Controllers::Battery& batteryController,
|
||||||
|
const Controllers::Ble& bleController,
|
||||||
|
Controllers::FS& fsController)
|
||||||
|
: dateTimeController(dateTimeController), batteryController(batteryController), bleController(bleController), fs(fsController) {
|
||||||
|
|
||||||
|
int result = fsController.FileOpen(&file, "program.bin", LFS_O_RDONLY);
|
||||||
|
asm_assert(result >= 0);
|
||||||
|
|
||||||
|
result = fsController.FileSeek(&file, 0, LFS_SEEK_END);
|
||||||
|
asm_assert(result >= 0);
|
||||||
|
program_size = result;
|
||||||
|
fsController.FileSeek(&file, 0, LFS_SEEK_SET);
|
||||||
|
|
||||||
|
populate_cache(0);
|
||||||
|
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASM::~ASM() {
|
||||||
|
if (taskRefresh != nullptr) {
|
||||||
|
lv_task_del(taskRefresh);
|
||||||
|
}
|
||||||
|
if (statusIcons) {
|
||||||
|
lv_obj_del(statusIcons->GetObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.FileClose(&file);
|
||||||
|
|
||||||
|
// We don't need to clean the screen since all objects are deleted when their shared_ptr is dropped
|
||||||
|
// lv_obj_clean(lv_scr_act());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASM::populate_cache(size_t pos) {
|
||||||
|
int result = fs.FileSeek(&file, pos);
|
||||||
|
asm_assert(result >= 0);
|
||||||
|
|
||||||
|
result = fs.FileRead(&file, cache, cache_size);
|
||||||
|
asm_assert(result >= 0);
|
||||||
|
|
||||||
|
cache_start = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASM::run() {
|
||||||
|
bool stop = false;
|
||||||
|
|
||||||
|
while (!stop && pc < program_size) {
|
||||||
|
OpcodeShort opcode = static_cast<OpcodeShort>(read_byte(pc));
|
||||||
|
if (static_cast<uint8_t>(opcode) & (1 << 7)) {
|
||||||
|
// Long opcode
|
||||||
|
OpcodeLong opcode = static_cast<OpcodeLong>(read_u16(pc));
|
||||||
|
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
default:
|
||||||
|
NRF_LOG_ERROR("Unknown opcode: 0x%04X", opcode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pc++;
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case OpcodeShort::WaitRefresh:
|
||||||
|
stop = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::Push0:
|
||||||
|
push(std::make_shared<ValueInteger>(0));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::PushU8:
|
||||||
|
push(std::make_shared<ValueInteger>(read_byte(pc)));
|
||||||
|
pc++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::PushU16:
|
||||||
|
push(std::make_shared<ValueInteger>(read_u16(pc)));
|
||||||
|
pc += 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::PushU24:
|
||||||
|
push(std::make_shared<ValueInteger>(read_u24(pc)));
|
||||||
|
pc += 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::PushU32:
|
||||||
|
push(std::make_shared<ValueInteger>(read_u32(pc)));
|
||||||
|
pc += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::PushEmptyString:
|
||||||
|
push(std::make_shared<ValueString>(new char[1] {0}, 1));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::Duplicate:
|
||||||
|
push(stack[stack_pointer - 1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::Pop:
|
||||||
|
pop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::LoadString: {
|
||||||
|
uint32_t ptr = pop_uint32();
|
||||||
|
|
||||||
|
int length = read_byte(ptr);
|
||||||
|
char* text = new char[length + 1];
|
||||||
|
text[length] = '\0';
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
text[i] = read_byte(ptr + 1 + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
push(std::make_shared<ValueString>(text, length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::StoreLocal:
|
||||||
|
locals[read_byte(pc++)] = pop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::LoadLocal:
|
||||||
|
push(locals[read_byte(pc++)]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::Branch: {
|
||||||
|
if (doBranch(pop_uint32()))
|
||||||
|
stop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::BranchIfTrue: {
|
||||||
|
uint32_t to = pop_uint32();
|
||||||
|
auto cond = pop();
|
||||||
|
|
||||||
|
if (cond->isTruthy() && doBranch(to))
|
||||||
|
stop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::Call: {
|
||||||
|
uint32_t next = pc;
|
||||||
|
pc = pop_uint32();
|
||||||
|
push(std::make_shared<ValueInteger>(next));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::StartPeriodicRefresh:
|
||||||
|
if (taskRefresh == nullptr) {
|
||||||
|
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::StopPeriodicRefresh:
|
||||||
|
if (taskRefresh != nullptr) {
|
||||||
|
lv_task_del(taskRefresh);
|
||||||
|
taskRefresh = nullptr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::SetLabelText: {
|
||||||
|
auto str = pop<ValueString>(String);
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
|
||||||
|
lv_label_set_text(obj->obj, str->str);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetArcRange: {
|
||||||
|
uint32_t max = pop_uint32();
|
||||||
|
uint32_t min = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_arc_set_range(obj->obj, min, max);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetArcRotation: {
|
||||||
|
uint32_t rot = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_arc_set_rotation(obj->obj, rot);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetArcBgAngles: {
|
||||||
|
uint32_t end = pop_uint32();
|
||||||
|
uint32_t start = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_arc_set_bg_angles(obj->obj, start, end);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetArcAdjustable: {
|
||||||
|
auto val = pop();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_arc_set_adjustable(obj->obj, val->isTruthy());
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetArcStartAngle: {
|
||||||
|
uint32_t angle = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_arc_set_start_angle(obj->obj, angle);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetArcValue: {
|
||||||
|
uint32_t value = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_arc_set_value(obj->obj, value);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::CreateLabel:
|
||||||
|
push(std::make_shared<ValueLvglObject>(lv_label_create(lv_scr_act(), NULL)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::CreateButton:
|
||||||
|
push(std::make_shared<ValueLvglObject>(lv_btn_create(lv_scr_act(), NULL)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::CreateArc:
|
||||||
|
push(std::make_shared<ValueLvglObject>(lv_arc_create(lv_scr_act(), NULL)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::SetObjectAlign: {
|
||||||
|
int16_t y = pop_uint32();
|
||||||
|
int16_t x = pop_uint32();
|
||||||
|
uint8_t align = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_obj_align(obj->obj, lv_scr_act(), align, x, y);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetObjectSize: {
|
||||||
|
int16_t h = pop_uint32();
|
||||||
|
int16_t w = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_obj_set_size(obj->obj, w, h);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetObjectParent: {
|
||||||
|
auto parent = pop<ValueLvglObject>(LvglObject);
|
||||||
|
auto child = pop<ValueLvglObject>(LvglObject);
|
||||||
|
lv_obj_set_parent(child->obj, parent->obj);
|
||||||
|
push(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetStyleLocalInt:
|
||||||
|
case OpcodeShort::SetStyleLocalFont:
|
||||||
|
case OpcodeShort::SetStyleLocalColor: {
|
||||||
|
uint32_t value = pop_uint32();
|
||||||
|
uint32_t prop = pop_uint32();
|
||||||
|
uint32_t part = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case OpcodeShort::SetStyleLocalInt:
|
||||||
|
_lv_obj_set_style_local_int(obj->obj, part, prop, value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::SetStyleLocalColor:
|
||||||
|
_lv_obj_set_style_local_color(obj->obj, part, prop, lv_color_hex(value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::SetStyleLocalFont: {
|
||||||
|
if (value < num_fonts) {
|
||||||
|
_lv_obj_set_style_local_ptr(obj->obj, part, prop, fonts[value]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::SetEventHandler: {
|
||||||
|
uint32_t cb_pc = pop_uint32();
|
||||||
|
auto obj = pop<ValueLvglObject>(LvglObject);
|
||||||
|
CallbackInfo* cb = new CallbackInfo {this, cb_pc};
|
||||||
|
|
||||||
|
lv_obj_set_user_data(obj->obj, cb);
|
||||||
|
lv_obj_set_event_cb(obj->obj, event_handler);
|
||||||
|
push(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::Add:
|
||||||
|
push(std::make_shared<ValueInteger>(pop_uint32() + pop_uint32()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::Subtract: {
|
||||||
|
uint32_t b = pop_uint32();
|
||||||
|
uint32_t a = pop_uint32();
|
||||||
|
push(std::make_shared<ValueInteger>(a - b));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::Multiply:
|
||||||
|
push(std::make_shared<ValueInteger>(pop_uint32() * pop_uint32()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::Divide: {
|
||||||
|
uint32_t b = pop_uint32();
|
||||||
|
uint32_t a = pop_uint32();
|
||||||
|
push(std::make_shared<ValueInteger>(a / b));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::GrowString: {
|
||||||
|
auto len = pop_uint32();
|
||||||
|
auto str = pop<ValueString>(String);
|
||||||
|
|
||||||
|
size_t new_cap = len + str->capacity;
|
||||||
|
|
||||||
|
char* new_str = new char[new_cap];
|
||||||
|
memcpy(new_str, str->str, str->capacity);
|
||||||
|
|
||||||
|
push(std::make_shared<ValueString>(new_str, new_cap));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::ClearString: {
|
||||||
|
auto str = pop<ValueString>(String);
|
||||||
|
if (str->capacity > 0)
|
||||||
|
str->str[0] = '\0';
|
||||||
|
|
||||||
|
push(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::Concat: {
|
||||||
|
auto b = pop();
|
||||||
|
auto a = pop();
|
||||||
|
|
||||||
|
if (a->type() == String && b->type() == String) {
|
||||||
|
auto aString = static_cast<ValueString*>(a.get());
|
||||||
|
auto bString = static_cast<ValueString*>(b.get());
|
||||||
|
|
||||||
|
int len_a = strlen(aString->str);
|
||||||
|
int len_b = strlen(bString->str);
|
||||||
|
|
||||||
|
size_t new_len = len_a + len_b + 1;
|
||||||
|
|
||||||
|
if (aString->capacity >= new_len) {
|
||||||
|
strcat(aString->str, bString->str);
|
||||||
|
|
||||||
|
push(a);
|
||||||
|
} else {
|
||||||
|
char* s = new char[new_len + 1];
|
||||||
|
strcpy(s, aString->str);
|
||||||
|
strcat(s, bString->str);
|
||||||
|
|
||||||
|
push(std::make_shared<ValueString>(s, new_len + 1));
|
||||||
|
}
|
||||||
|
} else if (a->type() == String && b->type() == Integer) {
|
||||||
|
auto aString = static_cast<ValueString*>(a.get());
|
||||||
|
auto bInt = static_cast<ValueInteger*>(b.get());
|
||||||
|
|
||||||
|
size_t aLen = strlen(aString->str);
|
||||||
|
size_t need_cap = aLen + 12 + 1;
|
||||||
|
|
||||||
|
if (aString->capacity - aLen >= need_cap) {
|
||||||
|
snprintf(aString->str + aLen, aString->capacity - aLen, "%lu", bInt->i);
|
||||||
|
|
||||||
|
push(a);
|
||||||
|
} else {
|
||||||
|
char* s = new char[need_cap];
|
||||||
|
memcpy(s, aString->str, aLen);
|
||||||
|
snprintf(s + aLen, need_cap - aLen, "%lu", bInt->i);
|
||||||
|
|
||||||
|
push(std::make_shared<ValueString>(s, need_cap));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asm_assert(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::PushCurrentDateTime: {
|
||||||
|
auto time = dateTimeController.CurrentDateTime();
|
||||||
|
std::tm tm {
|
||||||
|
.tm_sec = dateTimeController.Seconds(),
|
||||||
|
.tm_min = dateTimeController.Minutes(),
|
||||||
|
.tm_hour = dateTimeController.Hours(),
|
||||||
|
.tm_mday = dateTimeController.Day(),
|
||||||
|
.tm_mon = static_cast<int>(dateTimeController.Month()) - 1,
|
||||||
|
.tm_year = dateTimeController.Year() - 1900,
|
||||||
|
.tm_wday = static_cast<int>(dateTimeController.DayOfWeek()),
|
||||||
|
.tm_yday = dateTimeController.DayOfYear() - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
push(std::make_shared<ValueDateTime>(time, tm));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::PushCurrentTicks:
|
||||||
|
push(std::make_shared<ValueInteger>((xTaskGetTickCount() * configTICK_RATE_HZ) / 1000));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::FormatDateTime: {
|
||||||
|
auto fmt = pop<ValueString>(String);
|
||||||
|
auto time = pop<ValueDateTime>(DateTime);
|
||||||
|
|
||||||
|
constexpr int max_len = 16;
|
||||||
|
char* str = new char[max_len]; // TODO: Allow user to reuse string in stack
|
||||||
|
|
||||||
|
strftime(str, max_len, fmt->str, &time->tm);
|
||||||
|
|
||||||
|
push(std::make_shared<ValueString>(str, max_len));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::RealignObject:
|
||||||
|
lv_obj_realign(pop<ValueLvglObject>(LvglObject)->obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::ShowStatusIcons:
|
||||||
|
if (!statusIcons) {
|
||||||
|
statusIcons = std::make_unique<Widgets::StatusIcons>(batteryController, bleController);
|
||||||
|
statusIcons->Create();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::Equals: {
|
||||||
|
auto b = pop();
|
||||||
|
auto a = pop();
|
||||||
|
push(std::make_shared<ValueInteger>(a.get()->compare(b.get()) == 0 ? 1 : 0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::Greater: {
|
||||||
|
auto b = pop();
|
||||||
|
auto a = pop();
|
||||||
|
push(std::make_shared<ValueInteger>(a.get()->compare(b.get()) > 0 ? 1 : 0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::Lesser: {
|
||||||
|
auto b = pop();
|
||||||
|
auto a = pop();
|
||||||
|
push(std::make_shared<ValueInteger>(a.get()->compare(b.get()) < 0 ? 1 : 0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::Negate:
|
||||||
|
push(std::make_shared<ValueInteger>(pop().get()->isTruthy() ? 0 : 1));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpcodeShort::GetDateTimeHour: {
|
||||||
|
auto time = pop<ValueDateTime>(DateTime);
|
||||||
|
push(std::make_shared<ValueInteger>(time->tm.tm_hour));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::GetDateTimeMinute: {
|
||||||
|
auto time = pop<ValueDateTime>(DateTime);
|
||||||
|
push(std::make_shared<ValueInteger>(time->tm.tm_min));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpcodeShort::GetDateTimeSecond: {
|
||||||
|
auto time = pop<ValueDateTime>(DateTime);
|
||||||
|
push(std::make_shared<ValueInteger>(time->tm.tm_sec));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
NRF_LOG_ERROR("Unknown opcode: 0x%02X", opcode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASM::Refresh() {
|
||||||
|
run();
|
||||||
|
|
||||||
|
if (statusIcons) {
|
||||||
|
statusIcons->Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASM::_asm_assert(bool condition, const char* msg) {
|
||||||
|
if (!condition) {
|
||||||
|
// TODO: Handle better
|
||||||
|
|
||||||
|
if (msg)
|
||||||
|
NRF_LOG_ERROR("Assertion failed: %s", msg);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ASM::doBranch(uint32_t to) {
|
||||||
|
if ((to & handler_return_pc_mark) != 0) {
|
||||||
|
pc = to & ~handler_return_pc_mark;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pc = to;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASM::OnObjectEvent(lv_obj_t* obj, lv_event_t event) {
|
||||||
|
if (event != LV_EVENT_CLICKED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CallbackInfo* cb = static_cast<CallbackInfo*>(lv_obj_get_user_data(obj));
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
push(std::make_shared<ValueInteger>(pc | handler_return_pc_mark));
|
||||||
|
pc = cb->callback_pc;
|
||||||
|
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
}
|
319
src/displayapp/screens/ASM.h
Normal file
319
src/displayapp/screens/ASM.h
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "displayapp/apps/Apps.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "displayapp/widgets/StatusIcons.h"
|
||||||
|
#include "components/datetime/DateTimeController.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
#define STRINGIZE_DETAIL(x) #x
|
||||||
|
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
||||||
|
#define asm_assert(condition) _asm_assert(condition, __FILE__ ":" STRINGIZE(__LINE__) " " #condition)
|
||||||
|
#else
|
||||||
|
#define asm_assert(condition) _asm_assert(condition, NULL)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Applications {
|
||||||
|
namespace Screens {
|
||||||
|
class ASM : public Screen {
|
||||||
|
public:
|
||||||
|
ASM(Controllers::DateTime&, const Controllers::Battery&, const Controllers::Ble&, Controllers::FS&);
|
||||||
|
~ASM();
|
||||||
|
|
||||||
|
void Refresh() override;
|
||||||
|
|
||||||
|
void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int num_slots = 16;
|
||||||
|
static constexpr int max_locals = 16;
|
||||||
|
static constexpr int stack_size = 32;
|
||||||
|
static constexpr int cache_size = 16;
|
||||||
|
|
||||||
|
enum DataType : uint8_t { Integer, String, LvglObject, DateTime };
|
||||||
|
|
||||||
|
// TODO: Use fancy C++ type stuff
|
||||||
|
struct Value {
|
||||||
|
virtual DataType type() = 0;
|
||||||
|
virtual int compare(Value* other) = 0;
|
||||||
|
virtual bool isTruthy() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ValueInteger : public Value {
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
ValueInteger(uint32_t i) : i(i) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType type() override {
|
||||||
|
return Integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare(Value* other) override {
|
||||||
|
if (other->type() != Integer) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto otherInt = static_cast<ValueInteger*>(other)->i;
|
||||||
|
|
||||||
|
if (i < otherInt) {
|
||||||
|
return -1;
|
||||||
|
} else if (i > otherInt) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTruthy() override {
|
||||||
|
return i != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ValueString : public Value {
|
||||||
|
char* str;
|
||||||
|
uint16_t capacity;
|
||||||
|
|
||||||
|
ValueString(char* str, uint16_t cap) : str(str), capacity(cap) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~ValueString() {
|
||||||
|
delete[] str;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType type() override {
|
||||||
|
return String;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare(Value* other) override {
|
||||||
|
if (other->type() != String) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strcmp(str, static_cast<ValueString*>(other)->str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTruthy() override {
|
||||||
|
return capacity > 0 && str[0] != '\0';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ValueLvglObject : public Value {
|
||||||
|
lv_obj_t* obj;
|
||||||
|
|
||||||
|
ValueLvglObject(lv_obj_t* obj) : obj(obj) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~ValueLvglObject() {
|
||||||
|
lv_obj_del(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType type() override {
|
||||||
|
return LvglObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare(Value*) override {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTruthy() override {
|
||||||
|
return obj != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ValueDateTime : public Value {
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time;
|
||||||
|
std::tm tm;
|
||||||
|
|
||||||
|
ValueDateTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time, std::tm tm)
|
||||||
|
: time(time), tm(tm) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType type() override {
|
||||||
|
return DateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare(Value* other) override {
|
||||||
|
if (other->type() != DateTime) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto otherTime = static_cast<ValueDateTime*>(other)->time;
|
||||||
|
|
||||||
|
if (time < otherTime) {
|
||||||
|
return -1;
|
||||||
|
} else if (time > otherTime) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTruthy() override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OpcodeShort : uint8_t {
|
||||||
|
StoreLocal,
|
||||||
|
LoadLocal,
|
||||||
|
Branch,
|
||||||
|
BranchIfTrue,
|
||||||
|
Call,
|
||||||
|
Push0,
|
||||||
|
PushU8,
|
||||||
|
PushU16,
|
||||||
|
PushU24,
|
||||||
|
PushU32,
|
||||||
|
PushEmptyString,
|
||||||
|
PushCurrentDateTime,
|
||||||
|
PushCurrentTicks,
|
||||||
|
Duplicate,
|
||||||
|
Pop,
|
||||||
|
LoadString,
|
||||||
|
|
||||||
|
StartPeriodicRefresh,
|
||||||
|
StopPeriodicRefresh,
|
||||||
|
ShowStatusIcons,
|
||||||
|
|
||||||
|
CreateLabel,
|
||||||
|
CreateButton,
|
||||||
|
CreateArc,
|
||||||
|
SetLabelText,
|
||||||
|
SetArcRange,
|
||||||
|
SetArcRotation,
|
||||||
|
SetArcBgAngles,
|
||||||
|
SetArcAdjustable,
|
||||||
|
SetArcStartAngle,
|
||||||
|
SetArcValue,
|
||||||
|
SetObjectAlign,
|
||||||
|
SetObjectSize,
|
||||||
|
SetObjectParent,
|
||||||
|
SetStyleLocalInt,
|
||||||
|
SetStyleLocalColor,
|
||||||
|
SetStyleLocalOpa,
|
||||||
|
SetStyleLocalFont,
|
||||||
|
SetEventHandler,
|
||||||
|
RealignObject,
|
||||||
|
WaitRefresh,
|
||||||
|
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
Multiply,
|
||||||
|
Divide,
|
||||||
|
Equals,
|
||||||
|
Greater,
|
||||||
|
Lesser,
|
||||||
|
Negate,
|
||||||
|
|
||||||
|
GrowString,
|
||||||
|
ClearString,
|
||||||
|
Concat,
|
||||||
|
FormatDateTime,
|
||||||
|
|
||||||
|
GetDateTimeHour,
|
||||||
|
GetDateTimeMinute,
|
||||||
|
GetDateTimeSecond
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OpcodeLong : uint16_t {};
|
||||||
|
|
||||||
|
void populate_cache(size_t pos);
|
||||||
|
|
||||||
|
uint8_t read_byte(size_t pos) {
|
||||||
|
if (pos < cache_start || pos >= cache_start + cache_size) {
|
||||||
|
populate_cache(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache[pos - cache_start];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t read_u16(size_t pos) {
|
||||||
|
return static_cast<uint16_t>(read_byte(pos + 1) << 8 | read_byte(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read_u24(size_t pos) {
|
||||||
|
return static_cast<uint32_t>(read_byte(pos + 2) << 16 | read_byte(pos + 1) << 8 | read_byte(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read_u32(size_t pos) {
|
||||||
|
return static_cast<uint32_t>(read_byte(pos + 3) << 24 | read_byte(pos + 2) << 16 | read_byte(pos + 1) << 8 | read_byte(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs_file_t file;
|
||||||
|
uint8_t cache[cache_size];
|
||||||
|
size_t cache_start;
|
||||||
|
size_t program_size;
|
||||||
|
size_t pc = 0;
|
||||||
|
|
||||||
|
std::shared_ptr<Value> locals[max_locals] = {};
|
||||||
|
|
||||||
|
std::shared_ptr<Value> stack[stack_size] = {};
|
||||||
|
uint8_t stack_pointer = 0;
|
||||||
|
|
||||||
|
lv_task_t* taskRefresh = nullptr;
|
||||||
|
|
||||||
|
Controllers::DateTime& dateTimeController;
|
||||||
|
const Controllers::Battery& batteryController;
|
||||||
|
const Controllers::Ble& bleController;
|
||||||
|
Controllers::FS& fs;
|
||||||
|
std::unique_ptr<Widgets::StatusIcons> statusIcons;
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void _asm_assert(bool condition, const char* msg);
|
||||||
|
bool doBranch(uint32_t to);
|
||||||
|
|
||||||
|
std::shared_ptr<Value> pop() {
|
||||||
|
asm_assert(stack_pointer > 0);
|
||||||
|
|
||||||
|
stack_pointer--;
|
||||||
|
|
||||||
|
auto v = stack[stack_pointer];
|
||||||
|
stack[stack_pointer] = nullptr;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::shared_ptr<T> pop(DataType wantType)
|
||||||
|
requires(std::is_base_of_v<Value, T>)
|
||||||
|
{
|
||||||
|
auto v = pop();
|
||||||
|
asm_assert(v->type() == wantType);
|
||||||
|
|
||||||
|
return std::static_pointer_cast<T>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t pop_uint32() {
|
||||||
|
return pop<ValueInteger>(Integer)->i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(std::shared_ptr<Value> v) {
|
||||||
|
asm_assert(stack_pointer < stack_size);
|
||||||
|
|
||||||
|
stack[stack_pointer++] = v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::ASM> {
|
||||||
|
static constexpr Apps app = Apps::ASM;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::eye;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::ASM(controllers.dateTimeController,
|
||||||
|
controllers.batteryController,
|
||||||
|
controllers.bleController,
|
||||||
|
controllers.filesystem);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 56b17bf9f74096774944bcac0829adcd887d391e
|
|
Loading…
Reference in a new issue