mirror of
https://github.com/InfiniTimeOrg/InfiniTime.git
synced 2024-10-22 15:11:51 +02:00
Merge 21c3b1ebb0
into 3a0d673df4
This commit is contained in:
commit
36a2bb34b1
|
@ -397,6 +397,7 @@ list(APPEND SOURCE_FILES
|
|||
displayapp/screens/Alarm.cpp
|
||||
displayapp/screens/Styles.cpp
|
||||
displayapp/screens/WeatherSymbols.cpp
|
||||
displayapp/screens/ASM.cpp
|
||||
displayapp/Colors.cpp
|
||||
displayapp/widgets/Counter.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) {
|
||||
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) {
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Pinetime {
|
|||
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 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);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "displayapp/apps/Apps.h"
|
||||
#include "Controllers.h"
|
||||
|
||||
#include "displayapp/screens/ASM.h"
|
||||
#include "displayapp/screens/Alarm.h"
|
||||
#include "displayapp/screens/Dice.h"
|
||||
#include "displayapp/screens/Timer.h"
|
||||
|
|
|
@ -42,7 +42,8 @@ namespace Pinetime {
|
|||
SettingChimes,
|
||||
SettingShakeThreshold,
|
||||
SettingBluetooth,
|
||||
Error
|
||||
Error,
|
||||
ASM
|
||||
};
|
||||
|
||||
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")
|
||||
else ()
|
||||
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::Timer")
|
||||
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);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue