2022-10-12 23:44:22 +02:00
|
|
|
// ****************************************************************************
|
|
|
|
// dmcp.cpp DB48X project
|
|
|
|
// ****************************************************************************
|
|
|
|
//
|
|
|
|
// File Description:
|
|
|
|
//
|
|
|
|
// A fake DMCP implementation with the functions we use in the simulator
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// ****************************************************************************
|
|
|
|
// (C) 2022 Christophe de Dinechin <christophe@dinechin.org>
|
|
|
|
// This software is licensed under the terms outlined in LICENSE.txt
|
|
|
|
// ****************************************************************************
|
|
|
|
// This file is part of DB48X.
|
|
|
|
//
|
|
|
|
// DB48X is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms outlined in the LICENSE.txt file
|
|
|
|
//
|
|
|
|
// DB48X is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
// ****************************************************************************
|
|
|
|
|
2022-10-15 10:44:24 +02:00
|
|
|
#include "dmcp.h"
|
|
|
|
|
|
|
|
#include "dmcp_fonts.c"
|
|
|
|
#include "sim-rpl.h"
|
|
|
|
#include "sim-screen.h"
|
2022-10-19 12:56:19 +02:00
|
|
|
#include "sim-window.h"
|
2022-10-15 10:44:24 +02:00
|
|
|
#include "types.h"
|
2022-10-16 13:18:19 +02:00
|
|
|
#include "recorder.h"
|
2022-10-15 10:44:24 +02:00
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
#include <stdarg.h>
|
2022-10-15 10:44:24 +02:00
|
|
|
#include <stdio.h>
|
2022-10-14 00:09:37 +02:00
|
|
|
#include <sys/time.h>
|
2022-10-15 10:44:24 +02:00
|
|
|
#include <target.h>
|
2022-10-12 23:44:22 +02:00
|
|
|
|
2023-06-23 22:53:37 +02:00
|
|
|
#include <QFileDialog>
|
|
|
|
#include <QSettings>
|
2023-06-23 20:26:34 +02:00
|
|
|
#include <iostream>
|
files: Allow `STO` and `RCL` to write to filesystem
When the argument describing the variable is a text, interpret that as
a file path on the disk. By default, data is stored in in a directory
named `data`, which will be created if necessary. For example, you can
use `3 "foo.48s" STO` to store the value `3` in a file named
`data/foo.48s` on disk.
How the file is stored depends on the extension given to the file:
* For `.txt` files, the object is stored as text
* For `.48s` files, the object is stored as DB48X source code
* For `.48b` files, the object is stored in binary format
* For `.csv` files, the object is stored in comma-separated format.
The binary format used for `.48b` includes a 4-byte magic number
identifying a DB48X format, and a 4-byte checksum used to ensure
binary compatibility between the firmware and the disk format.
At least during early days of development (prior to 1.0), it is quite
unlikely that the binary format for one version of the firmware would
be readable or writable by another version. If you need to recover
data from another version, you need to install that version and save
the object again in `.48s` (text) format.
Fixes: #375
Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
2023-11-14 07:23:58 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2023-06-23 20:26:34 +02:00
|
|
|
|
2022-10-14 09:10:34 +02:00
|
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
|
|
|
2022-10-16 13:18:19 +02:00
|
|
|
RECORDER(dmcp, 64, "DMCP system calls");
|
|
|
|
RECORDER(dmcp_error, 64, "DMCP errors");
|
|
|
|
RECORDER(dmcp_warning, 64, "DMCP warnings");
|
|
|
|
RECORDER(dmcp_notyet, 64, "DMCP features that are not yet implemented");
|
|
|
|
RECORDER(keys, 64, "DMCP key handling");
|
2023-07-27 13:27:14 +02:00
|
|
|
RECORDER(keys_empty, 64, "DMCP key_empty() call");
|
2022-10-16 13:18:19 +02:00
|
|
|
RECORDER(keys_warning, 64, "Warnings related to key handling");
|
|
|
|
RECORDER(lcd, 64, "DMCP lcd/display functions");
|
2023-07-16 15:44:01 +02:00
|
|
|
RECORDER(lcd_refresh, 64, "DMCP lcd/display refresh");
|
2022-10-16 13:18:19 +02:00
|
|
|
RECORDER(lcd_width, 64, "Width of strings and chars");
|
|
|
|
RECORDER(lcd_warning, 64, "Warnings from lcd/display functions");
|
2022-10-14 09:10:34 +02:00
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
#undef ppgm_fp
|
|
|
|
|
2023-07-27 13:27:14 +02:00
|
|
|
volatile int lcd_needsupdate = 0;
|
|
|
|
int lcd_buf_cleared = 0;
|
|
|
|
uint8_t lcd_buffer[LCD_SCANLINE * LCD_H / 8];
|
|
|
|
bool shiftHeld = false;
|
|
|
|
bool altHeld = false;
|
2022-10-13 22:36:25 +02:00
|
|
|
|
|
|
|
static disp_stat_t t20_ds = { .f = &lib_mono_10x17 };
|
|
|
|
static disp_stat_t t24_ds = { .f = &lib_mono_12x20 };
|
|
|
|
static disp_stat_t fReg_ds = { .f = &lib_mono_17x25 };
|
|
|
|
static FIL ppgm_fp_file;
|
|
|
|
|
|
|
|
sys_sdb_t sdb =
|
|
|
|
{
|
|
|
|
.ppgm_fp = &ppgm_fp_file,
|
|
|
|
.pds_t20 = &t20_ds,
|
|
|
|
.pds_t24 = &t24_ds,
|
|
|
|
.pds_fReg = &fReg_ds,
|
|
|
|
};
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
|
2022-10-14 13:20:07 +02:00
|
|
|
void LCD_power_off(int UNUSED clear)
|
2022-10-12 23:44:22 +02:00
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp, "LCD_power_off");
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LCD_power_on()
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp, "LCD_power_on");
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
2022-10-26 08:17:47 +02:00
|
|
|
uint32_t read_power_voltage()
|
|
|
|
{
|
2023-10-29 22:48:54 +01:00
|
|
|
return BATTERY_VOFF + 25 + sys_current_ms() % (BATTERY_VMAX - BATTERY_VMIN);
|
2022-10-26 08:17:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int get_lowbat_state()
|
|
|
|
{
|
|
|
|
return read_power_voltage() < 2300;
|
|
|
|
}
|
|
|
|
|
|
|
|
int usb_powered()
|
|
|
|
{
|
2023-07-19 13:05:46 +02:00
|
|
|
return sys_current_ms() / 10000 % 3;
|
2022-10-26 08:17:47 +02:00
|
|
|
}
|
2022-10-12 23:44:22 +02:00
|
|
|
|
|
|
|
int create_screenshot(int report_error)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet,
|
|
|
|
"create_screenshot(%d) not implemented", report_error);
|
2022-10-13 22:36:25 +02:00
|
|
|
return 0;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void draw_power_off_image(int allow_errors)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet,
|
|
|
|
"draw_power_off_image(%d) not implemented", allow_errors);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2023-06-23 19:50:21 +02:00
|
|
|
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
int handle_menu(const smenu_t * menu_id, int action, int cur_line)
|
|
|
|
{
|
2023-06-23 20:26:34 +02:00
|
|
|
uint menu_line = 0;
|
2023-06-23 19:50:21 +02:00
|
|
|
bool done = false;
|
|
|
|
|
|
|
|
while (!done)
|
|
|
|
{
|
|
|
|
t24->xoffs = 0;
|
|
|
|
lcd_writeClr(t24);
|
|
|
|
lcd_writeClr(t20);
|
|
|
|
lcd_clear_buf();
|
|
|
|
lcd_putsR(t20, menu_id->name);
|
|
|
|
|
|
|
|
char buf[80];
|
|
|
|
uint count = 0;
|
|
|
|
while (menu_id->items[count])
|
|
|
|
count++;
|
|
|
|
|
2023-06-23 20:26:34 +02:00
|
|
|
for (uint i = 0; i < count; i++)
|
2023-06-23 19:50:21 +02:00
|
|
|
{
|
|
|
|
uint8_t mid = menu_id->items[i];
|
|
|
|
cstring label = menu_line_str_app(mid, buf, sizeof(buf));
|
|
|
|
if (!label)
|
|
|
|
{
|
|
|
|
label = "Unimplemented DMCP menu";
|
|
|
|
switch(mid)
|
|
|
|
{
|
|
|
|
case MI_MSC: label = "Activate USB Disk"; break;
|
|
|
|
case MI_PGM_LOAD: label = "Load Program"; break;
|
|
|
|
case MI_LOAD_QSPI: label = "Load QSPI from FAT"; break;
|
|
|
|
case MI_SYSTEM_ENTER: label = "System >"; break;
|
|
|
|
case MI_SET_TIME: label = "Set Time >"; break;
|
|
|
|
case MI_SET_DATE: label = "Set Date >"; break;
|
|
|
|
case MI_BEEP_MUTE: label = "Beep Mute"; break;
|
|
|
|
case MI_SLOW_AUTOREP: label = "Slow Autorepeat"; break;
|
|
|
|
case MI_DISK_INFO: label = "Show Disk Info"; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t24->inv = i == menu_line;
|
|
|
|
lcd_printAt(t24, i+1, "%u. %s", i+1, label);
|
|
|
|
}
|
|
|
|
lcd_refresh();
|
|
|
|
|
|
|
|
bool redraw = false;
|
|
|
|
while (!redraw)
|
|
|
|
{
|
|
|
|
while (key_empty())
|
|
|
|
sys_sleep();
|
|
|
|
|
|
|
|
int key = key_pop();
|
|
|
|
switch (key)
|
|
|
|
{
|
|
|
|
case KEY_UP:
|
|
|
|
if (menu_line > 0)
|
|
|
|
{
|
|
|
|
menu_line--;
|
|
|
|
redraw = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KEY_DOWN:
|
|
|
|
if (menu_line + 1 < count)
|
|
|
|
{
|
|
|
|
menu_line++;
|
|
|
|
redraw = true;
|
|
|
|
}
|
|
|
|
break;
|
2023-06-23 23:58:38 +02:00
|
|
|
case -1:
|
|
|
|
// Signals that main application is exiting, leave all dialogs
|
2023-06-23 19:50:21 +02:00
|
|
|
case KEY_EXIT:
|
|
|
|
redraw = true;
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KEY_ENTER:
|
|
|
|
run_menu_item_app(menu_id->items[menu_line]);
|
|
|
|
redraw = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
return 0;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
2023-07-16 15:44:01 +02:00
|
|
|
volatile int8_t keys[4] = { 0 };
|
|
|
|
volatile uint keyrd = 0;
|
|
|
|
volatile uint keywr = 0;
|
|
|
|
enum { nkeys = sizeof(keys) / sizeof(keys[0]) };
|
2022-10-12 23:44:22 +02:00
|
|
|
|
|
|
|
int key_empty()
|
|
|
|
{
|
2023-08-27 23:06:24 +02:00
|
|
|
static bool empty = true;
|
|
|
|
if ((keyrd == keywr) != empty)
|
|
|
|
{
|
|
|
|
record(keys_empty,
|
|
|
|
"Key empty %u-%u = %+s",
|
|
|
|
keyrd,
|
|
|
|
keywr,
|
|
|
|
keyrd == keywr ? "empty" : "full");
|
|
|
|
empty = keyrd == keywr;
|
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
return keyrd == keywr;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-18 21:28:59 +02:00
|
|
|
|
|
|
|
int key_remaining()
|
|
|
|
{
|
|
|
|
return nkeys - (keywr - keyrd);
|
|
|
|
}
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
int key_pop()
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
if (keyrd != keywr)
|
2022-10-16 13:18:19 +02:00
|
|
|
{
|
2023-07-16 15:44:01 +02:00
|
|
|
record(keys, "Key %d (rd %u wr %u)", keys[keyrd % nkeys], keyrd, keywr);
|
2022-10-13 22:36:25 +02:00
|
|
|
return keys[keyrd++ % nkeys];
|
2022-10-16 13:18:19 +02:00
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
return -1;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
2022-10-29 19:10:34 +02:00
|
|
|
int key_tail()
|
|
|
|
{
|
|
|
|
if (keyrd != keywr)
|
2023-09-15 07:18:15 +02:00
|
|
|
return keys[(keyrd + nkeys - 1) % nkeys];
|
2022-10-29 19:10:34 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
int key_pop_last()
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
if (keywr - keyrd > 1)
|
|
|
|
keyrd = keywr - 1;
|
|
|
|
if (keyrd != keywr)
|
|
|
|
return keys[keyrd++ % nkeys];
|
|
|
|
return -1;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
void key_pop_all()
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
keyrd = 0;
|
|
|
|
keywr = 0;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-16 13:18:19 +02:00
|
|
|
int key_push(int k)
|
2022-10-12 23:44:22 +02:00
|
|
|
{
|
2023-07-27 13:27:14 +02:00
|
|
|
record(keys, "Push key %d (wr %u rd %u) shifts=%+s",
|
|
|
|
k, keywr, keyrd,
|
|
|
|
shiftHeld ? altHeld ? "Shift+Alt" : "Shift"
|
|
|
|
: altHeld ? "Alt" : "None");
|
2022-10-19 12:56:19 +02:00
|
|
|
MainWindow::theMainWindow()->pushKey(k);
|
2022-10-13 22:36:25 +02:00
|
|
|
if (keywr - keyrd < nkeys)
|
2022-10-16 13:18:19 +02:00
|
|
|
keys[keywr++ % nkeys] = k;
|
|
|
|
else
|
|
|
|
record(keys_warning, "Dropped key %d (wr %u rd %u)", k, keywr, keyrd);
|
2023-07-16 15:44:01 +02:00
|
|
|
record(keys, "Pushed key %d (wr %u rd %u)", k, keywr, keyrd);
|
2022-10-13 22:36:25 +02:00
|
|
|
return keywr - keyrd < nkeys;
|
|
|
|
}
|
2023-07-27 00:17:47 +02:00
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
int read_key(int *k1, int *k2)
|
|
|
|
{
|
2023-07-27 00:17:47 +02:00
|
|
|
uint count = keywr - keyrd;
|
2023-07-27 13:27:14 +02:00
|
|
|
if (shiftHeld || altHeld)
|
|
|
|
{
|
|
|
|
*k1 = keys[(keywr - 1) % nkeys];
|
|
|
|
if (*k1)
|
|
|
|
{
|
|
|
|
*k2 = shiftHeld ? KEY_UP : KEY_DOWN;
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-27 00:17:47 +02:00
|
|
|
if (count > 1)
|
|
|
|
{
|
|
|
|
*k1 = keys[(keywr - 2) % nkeys];
|
|
|
|
*k2 = keys[(keywr - 1) % nkeys];
|
2023-07-27 13:27:14 +02:00
|
|
|
record(keys, "read_key has two keys %d and %d", *k1, *k2);
|
2023-07-27 00:17:47 +02:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
*k1 = keys[(keywr - 1) % nkeys];
|
|
|
|
*k2 = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
*k1 = *k2 = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sys_last_key()
|
|
|
|
{
|
|
|
|
return keys[(keywr - 1) % nkeys];
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2023-06-23 18:31:57 +02:00
|
|
|
|
|
|
|
int runner_get_key(int *repeat)
|
|
|
|
{
|
|
|
|
return repeat ? key_pop_last() : key_pop();
|
|
|
|
}
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
void lcd_clear_buf()
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd, "Clearing buffer");
|
2022-10-13 22:36:25 +02:00
|
|
|
for (unsigned i = 0; i < sizeof(lcd_buffer); i++)
|
2022-10-15 03:51:54 +02:00
|
|
|
lcd_buffer[i] = 0xFF;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
2023-06-24 00:35:50 +02:00
|
|
|
static uint32_t last_warning = 0;
|
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
inline void lcd_set_pixel(int x, int y)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
if (x < 0 || x > LCD_W || y < 0 || y > LCD_H)
|
|
|
|
{
|
2023-06-24 00:35:50 +02:00
|
|
|
uint now = sys_current_ms();
|
|
|
|
if (now - last_warning > 1000)
|
|
|
|
{
|
|
|
|
record(lcd_warning, "Clearing pixel at (%d, %d)", x, y);
|
|
|
|
last_warning = now;
|
|
|
|
}
|
2022-10-16 13:18:19 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
unsigned bo = y * LCD_SCANLINE + (LCD_W - x);
|
|
|
|
if (bo/8 < sizeof(lcd_buffer))
|
|
|
|
lcd_buffer[bo / 8] |= (1 << (bo % 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void lcd_clear_pixel(int x, int y)
|
|
|
|
{
|
|
|
|
|
2022-10-16 13:18:19 +02:00
|
|
|
if (x < 0 || x > LCD_W || y < 0 || y > LCD_H)
|
|
|
|
{
|
2023-06-24 00:35:50 +02:00
|
|
|
uint now = sys_current_ms();
|
|
|
|
if (now - last_warning > 1000)
|
|
|
|
{
|
|
|
|
record(lcd_warning, "Setting pixel at (%d, %d)", x, y);
|
|
|
|
last_warning = now;
|
|
|
|
}
|
2022-10-16 13:18:19 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
unsigned bo = y * LCD_SCANLINE + (LCD_W - x);
|
|
|
|
if (bo/8 < sizeof(lcd_buffer))
|
|
|
|
lcd_buffer[bo / 8] &= ~(1 << (bo % 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void lcd_pixel(int x, int y, int val)
|
|
|
|
{
|
2022-10-15 03:51:54 +02:00
|
|
|
if (!val)
|
2022-10-13 22:36:25 +02:00
|
|
|
lcd_set_pixel(x, y);
|
|
|
|
else
|
|
|
|
lcd_clear_pixel(x, y);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void lcd_draw_menu_keys(const char *keys[])
|
|
|
|
{
|
2022-10-13 23:28:25 +02:00
|
|
|
int my = LCD_H - t20->f->height - 4;
|
2022-10-13 22:36:25 +02:00
|
|
|
int mh = t20->f->height + 2;
|
|
|
|
int mw = (LCD_W - 10) / 6;
|
2022-10-13 23:28:25 +02:00
|
|
|
int sp = (LCD_W - 5) - 6 * mw;
|
2022-10-13 22:36:25 +02:00
|
|
|
|
|
|
|
t20->inv = 1;
|
|
|
|
t20->lnfill = 0;
|
|
|
|
t20->bgfill = 1;
|
|
|
|
t20->newln = 0;
|
|
|
|
t20->y = my + 1;
|
2022-10-16 16:29:45 +02:00
|
|
|
|
|
|
|
record(lcd, "Menu [%s][%s][%s][%s][%s][%s]",
|
|
|
|
keys[0], keys[1], keys[2], keys[3], keys[4], keys[5]);
|
2022-10-13 22:36:25 +02:00
|
|
|
for (int m = 0; m < 6; m++)
|
|
|
|
{
|
2022-10-13 23:28:25 +02:00
|
|
|
int x = (2 * m + 1) * mw / 2 + (m * sp) / 5 + 2;
|
|
|
|
lcd_fill_rect(x - mw/2+2, my, mw-4, mh, 1);
|
|
|
|
lcd_fill_rect(x - mw/2+1, my+1, mw-2, mh-2, 1);
|
|
|
|
lcd_fill_rect(x - mw/2, my+2, mw, mh-4, 1);
|
|
|
|
|
|
|
|
// Truncate the menu to fit
|
|
|
|
// Note that DMCP is NOT robust to overflow here and can die
|
|
|
|
int size = 11;
|
|
|
|
int w = 0;
|
|
|
|
char buffer[12];
|
|
|
|
do
|
|
|
|
{
|
|
|
|
snprintf(buffer, sizeof(buffer), "%.*s", size, keys[m]);
|
|
|
|
w = lcd_textWidth(t20, buffer);
|
|
|
|
size--;
|
|
|
|
} while (w > mw);
|
|
|
|
|
2022-10-14 09:03:35 +02:00
|
|
|
if (size < (int) strlen(keys[m]))
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd_warning,
|
|
|
|
"Menu entry %d [%s] is too long "
|
|
|
|
"(%d chars lost, shows as [%s])",
|
|
|
|
m, keys[m], (int) strlen(keys[m]) - size + 1, buffer);
|
2022-10-12 23:44:22 +02:00
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
t20->x = x - w / 2;
|
2022-10-13 23:28:25 +02:00
|
|
|
lcd_puts(t20, buffer);
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
t20->lnfill = 1;
|
|
|
|
t20->inv = 0;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
|
2022-10-16 13:18:19 +02:00
|
|
|
void lcd_fill_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, int val)
|
2022-10-12 23:44:22 +02:00
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
if (val)
|
|
|
|
record(lcd, "Fill rectangle (%u,%u) + (%u, %u)", x, y, w, h);
|
|
|
|
else
|
|
|
|
record(lcd, "Clear rectangle (%u,%u) + (%u, %u)", x, y, w, h);
|
|
|
|
|
|
|
|
if (x + w > LCD_W)
|
|
|
|
{
|
|
|
|
record(lcd_warning,
|
|
|
|
"Rectangle X is outside screen (%u, %u) + (%u, %u)",
|
|
|
|
x, y, w, h);
|
|
|
|
w = LCD_W - x;
|
|
|
|
if (w > LCD_W)
|
|
|
|
x = w = 0;
|
|
|
|
}
|
|
|
|
if (y +h > LCD_H)
|
|
|
|
{
|
|
|
|
record(lcd_warning,
|
|
|
|
"Rectangle Y is outside screen (%u, %u) + (%u, %u)",
|
|
|
|
x, y, w, h);
|
|
|
|
h = LCD_W - y;
|
|
|
|
if (h > LCD_W)
|
|
|
|
y = h = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint r = y; r < y + h; r++)
|
|
|
|
for (uint c = x; c < x + w; c++)
|
2022-10-13 22:36:25 +02:00
|
|
|
lcd_pixel(c, r, val);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
int lcd_fontWidth(disp_stat_t * ds)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
return ds->f->width;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
int lcd_for_calc(int what)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "lcd_for_calc %d not implemented", what);
|
2022-10-13 22:36:25 +02:00
|
|
|
return 0;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
int lcd_get_buf_cleared()
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd, "get_buf_cleared returns %d", lcd_buf_cleared);
|
2022-10-13 22:36:25 +02:00
|
|
|
return lcd_buf_cleared;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
int lcd_lineHeight(disp_stat_t * ds)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
return ds->f->height;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
uint8_t * lcd_line_addr(int y)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
if (y < 0 || y > LCD_H)
|
|
|
|
{
|
|
|
|
record(lcd_warning, "lcd_line_addr(%d), line is out of range", y);
|
|
|
|
y = 0;
|
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
unsigned offset = y * LCD_SCANLINE / 8;
|
|
|
|
return lcd_buffer + offset;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-24 20:08:55 +02:00
|
|
|
int lcd_toggleFontT(int nr)
|
|
|
|
{
|
|
|
|
return nr;
|
|
|
|
}
|
2022-10-12 23:44:22 +02:00
|
|
|
int lcd_nextFontNr(int nr)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
if (nr < (int) dmcp_fonts_count - 1)
|
|
|
|
nr++;
|
|
|
|
else
|
|
|
|
nr = dmcp_fonts_count - 1;
|
|
|
|
return nr;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
int lcd_prevFontNr(int nr)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
if (nr > 0)
|
|
|
|
nr--;
|
|
|
|
else
|
|
|
|
nr = 0;
|
|
|
|
return nr;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_prevLn(disp_stat_t * ds)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
ds->y -= lcd_lineHeight(ds);
|
|
|
|
ds->x = ds->xoffs;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_print(disp_stat_t * ds, const char* fmt, ...)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
static char buffer[256];
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
lcd_puts(ds, buffer);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
2022-10-16 13:18:19 +02:00
|
|
|
void lcd_forced_refresh()
|
|
|
|
{
|
|
|
|
record(lcd, "Forced refresh");
|
2022-10-18 21:28:59 +02:00
|
|
|
lcd_needsupdate++;
|
2022-10-16 13:18:19 +02:00
|
|
|
}
|
2022-10-12 23:44:22 +02:00
|
|
|
void lcd_refresh()
|
|
|
|
{
|
2023-07-16 15:44:01 +02:00
|
|
|
record(lcd_refresh, "Refresh %u", lcd_needsupdate);
|
2022-10-18 21:28:59 +02:00
|
|
|
lcd_needsupdate++;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_refresh_dma()
|
|
|
|
{
|
2023-07-16 15:44:01 +02:00
|
|
|
record(lcd_refresh, "Refresh DMA %u", lcd_needsupdate);
|
2022-10-18 21:28:59 +02:00
|
|
|
lcd_needsupdate++;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_refresh_wait()
|
|
|
|
{
|
2023-07-16 15:44:01 +02:00
|
|
|
record(lcd_refresh, "Refresh wait %u", lcd_needsupdate);
|
2022-10-18 21:28:59 +02:00
|
|
|
lcd_needsupdate++;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_refresh_lines(int ln, int cnt)
|
|
|
|
{
|
2023-07-16 15:44:01 +02:00
|
|
|
record(lcd_refresh, "Refresh lines %u (%d-%d) count %d",
|
|
|
|
lcd_needsupdate, ln, ln+cnt-1, cnt);
|
2022-10-18 21:28:59 +02:00
|
|
|
lcd_needsupdate += (ln >= 0 && cnt > 0);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_setLine(disp_stat_t * ds, int ln_nr)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
ds->x = ds->xoffs;;
|
|
|
|
ds->y = ln_nr * lcd_lineHeight(ds);
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd, "set line %u coord (%d, %d)", ln_nr, ds->x, ds->y);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void lcd_setXY(disp_stat_t * ds, int x, int y)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd, "set XY (%d, %d)", x, y);
|
2022-10-13 22:36:25 +02:00
|
|
|
ds->x = x;
|
|
|
|
ds->y = y;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_set_buf_cleared(int val)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd, "Set buffer cleared %d", val);
|
2022-10-13 22:36:25 +02:00
|
|
|
lcd_buf_cleared = val;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_switchFont(disp_stat_t * ds, int nr)
|
|
|
|
{
|
2023-06-23 22:53:37 +02:00
|
|
|
record(lcd, "Selected font %d", nr);
|
2022-10-13 22:36:25 +02:00
|
|
|
if (nr >= 0 && nr <= (int) dmcp_fonts_count)
|
|
|
|
ds->f = dmcp_fonts[nr];
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-14 21:47:32 +02:00
|
|
|
|
|
|
|
int lcd_charWidth(disp_stat_t * ds, int c)
|
|
|
|
{
|
|
|
|
int width = 0;
|
|
|
|
const line_font_t *f = ds->f;
|
|
|
|
byte first = f->first_char;
|
|
|
|
byte count = f->char_cnt;
|
|
|
|
const uint16_t *offs = f->offs;
|
|
|
|
const uint8_t *data = f->data;
|
|
|
|
uint xspc = ds->xspc;
|
|
|
|
|
|
|
|
c -= first;
|
|
|
|
if (c >= 0 && c < count)
|
|
|
|
{
|
|
|
|
uint off = offs[c];
|
|
|
|
width += data[off + 0] + data[off + 2] + xspc;
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd_width,
|
2022-10-16 16:29:45 +02:00
|
|
|
"Character width of %c (%d=0x%x) is %d",
|
|
|
|
c + first, c + first, c + first, width);
|
2022-10-16 13:18:19 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
record(lcd_width, "Character width of nonexistent %d is %d", c, width);
|
2022-10-14 21:47:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
int lcd_textWidth(disp_stat_t * ds, const char* text)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
int width = 0;
|
2022-10-14 09:03:35 +02:00
|
|
|
byte c;
|
2022-10-13 22:36:25 +02:00
|
|
|
const line_font_t *f = ds->f;
|
2022-10-14 09:03:35 +02:00
|
|
|
byte first = f->first_char;
|
|
|
|
byte count = f->char_cnt;
|
2022-10-13 22:36:25 +02:00
|
|
|
const uint16_t *offs = f->offs;
|
|
|
|
const uint8_t *data = f->data;
|
|
|
|
uint xspc = ds->xspc;
|
2022-10-16 13:18:19 +02:00
|
|
|
const byte *p = (const byte *) text;
|
2022-10-13 22:36:25 +02:00
|
|
|
|
2022-10-16 13:18:19 +02:00
|
|
|
while ((c = *p++))
|
2022-10-13 22:36:25 +02:00
|
|
|
{
|
|
|
|
c -= first;
|
|
|
|
if (c >= 0 && c < count)
|
|
|
|
{
|
|
|
|
uint off = offs[c];
|
|
|
|
width += data[off + 0] + data[off + 2] + xspc;
|
|
|
|
}
|
2022-10-16 13:18:19 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
record(lcd_width,
|
|
|
|
"Nonexistent character %d at offset %d in [%s]",
|
|
|
|
c + first, p - (const byte *) text, text);
|
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
return width;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
|
|
|
|
void lcd_writeClr(disp_stat_t *ds)
|
2022-10-12 23:44:22 +02:00
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd, "Clearing display state"); // Not sure this is what it does
|
2023-06-23 19:50:21 +02:00
|
|
|
ds->x = 0; // ds->xoffs;
|
2022-10-13 22:36:25 +02:00
|
|
|
ds->y = 0;
|
|
|
|
ds->inv = 0;
|
|
|
|
ds->bgfill = 1;
|
|
|
|
ds->lnfill = 1;
|
2023-06-23 19:50:21 +02:00
|
|
|
ds->newln = 1;
|
2022-10-13 23:28:25 +02:00
|
|
|
ds->xspc = 1;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
2022-10-15 03:49:05 +02:00
|
|
|
void lcd_writeNl(disp_stat_t *ds)
|
|
|
|
{
|
|
|
|
ds->x = ds->xoffs;
|
|
|
|
ds->y += lcd_lineHeight(ds);
|
2022-10-16 13:18:19 +02:00
|
|
|
record(lcd, "New line, now at (%d, %d)", ds->x, ds->y);
|
2022-10-15 03:49:05 +02:00
|
|
|
}
|
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
inline void lcd_writeTextInternal(disp_stat_t *ds, const char *text, int write)
|
|
|
|
{
|
|
|
|
uint c;
|
|
|
|
const line_font_t *f = ds->f;
|
|
|
|
uint first = f->first_char;
|
|
|
|
uint count = f->char_cnt;
|
|
|
|
uint height = f->height;
|
|
|
|
const uint8_t *data = f->data;
|
|
|
|
const uint16_t *offs = f->offs;
|
2022-10-13 23:28:25 +02:00
|
|
|
int xspc = ds->xspc;
|
|
|
|
int x = ds->x + xspc;
|
2022-10-13 22:36:25 +02:00
|
|
|
int y = ds->y + ds->ln_offs;
|
|
|
|
int inv = ds->inv != 0;
|
2022-10-16 13:18:19 +02:00
|
|
|
const byte *p = (const byte *) text;
|
|
|
|
|
|
|
|
if (write)
|
|
|
|
record(lcd, "Write text [%s] at (%d, %d)", text, x, y);
|
|
|
|
else
|
|
|
|
record(lcd, "Skip text [%s] at (%d, %d)", text, x, y);
|
2022-10-13 22:36:25 +02:00
|
|
|
|
|
|
|
if (ds->lnfill)
|
|
|
|
lcd_fill_rect(ds->xoffs, y, LCD_W, height, inv);
|
|
|
|
|
2022-10-16 13:18:19 +02:00
|
|
|
while ((c = *p++))
|
2022-10-13 22:36:25 +02:00
|
|
|
{
|
|
|
|
c -= first;
|
|
|
|
if (c < count)
|
|
|
|
{
|
|
|
|
int off = offs[c];
|
|
|
|
const uint8_t *dp = data + off;
|
|
|
|
int cx = *dp++;
|
|
|
|
int cy = *dp++;
|
|
|
|
int cols = *dp++;
|
|
|
|
int rows = *dp++;
|
|
|
|
|
|
|
|
if (!write)
|
|
|
|
{
|
|
|
|
x += cx + cols;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int r = 0; r < cy; r++)
|
|
|
|
for (int c = 0; c < cx + cols; c++)
|
|
|
|
lcd_pixel(x+c, y+r, inv);
|
|
|
|
|
|
|
|
for (int r = 0; r < rows; r++)
|
|
|
|
{
|
|
|
|
int data = 0;
|
|
|
|
for (int c = 0; c < cols; c += 8)
|
|
|
|
data |= *dp++ << c;
|
|
|
|
|
|
|
|
for (int c = 0; c < cx; c++)
|
|
|
|
lcd_pixel(x+c, y+r, inv);
|
|
|
|
|
|
|
|
for (int c = 0; c < cols; c++)
|
|
|
|
{
|
|
|
|
int val = (data >> (cols - c - 1)) & 1;
|
|
|
|
if (val || ds->bgfill)
|
|
|
|
lcd_pixel(x + c + cx, y + r + cy, val != inv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-14 09:03:35 +02:00
|
|
|
for (uint r = cy + rows; r < height; r++)
|
2022-10-13 22:36:25 +02:00
|
|
|
for (int c = 0; c < cx + cols; c++)
|
|
|
|
lcd_pixel(x+c, y+r, inv);
|
|
|
|
|
|
|
|
|
|
|
|
x += cx + cols + xspc;
|
|
|
|
}
|
2022-10-13 23:28:25 +02:00
|
|
|
else
|
|
|
|
{
|
2022-10-16 16:33:40 +02:00
|
|
|
record(lcd_warning,
|
|
|
|
"Nonexistent character [%d] in [%s] at %d, max=%d",
|
|
|
|
c + first, text, p - (byte_p) text, count + first);
|
2022-10-13 23:28:25 +02:00
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
ds->x = x;
|
|
|
|
if (ds->newln)
|
|
|
|
{
|
|
|
|
ds->x = ds->xoffs;
|
|
|
|
ds->y += height;
|
|
|
|
}
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-13 22:36:25 +02:00
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
void lcd_writeText(disp_stat_t * ds, const char* text)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
lcd_writeTextInternal(ds, text, 1);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void lcd_writeTextWidth(disp_stat_t * ds, const char* text)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
lcd_writeTextInternal(ds, text, 0);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void reset_auto_off()
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
// No effect
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void rtc_wakeup_delay()
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "rtc_wakeup_delay not implemented");
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void run_help_file(const char * help_file)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "run_help_file not implemented");
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void run_help_file_style(const char * help_file, user_style_fn_t *user_style_fn)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "run_help_file_style not implemented");
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void start_buzzer_freq(uint32_t freq)
|
|
|
|
{
|
2023-07-17 09:51:36 +02:00
|
|
|
record(dmcp_error, "start_buzzer");
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void stop_buzzer()
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "stop_buzzer not implemented");
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-11-02 13:37:04 +01:00
|
|
|
|
|
|
|
int sys_free_mem()
|
|
|
|
{
|
|
|
|
// On the simulator, we have real memory
|
|
|
|
return 1024 * 1024;
|
|
|
|
}
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
void sys_delay(uint32_t ms_delay)
|
|
|
|
{
|
2022-10-13 22:36:25 +02:00
|
|
|
QThread::msleep(ms_delay);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-15 10:44:24 +02:00
|
|
|
|
|
|
|
static struct timer
|
|
|
|
{
|
|
|
|
uint32_t deadline;
|
|
|
|
bool enabled;
|
|
|
|
} timers[4];
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
void sys_sleep()
|
|
|
|
{
|
2022-10-15 10:44:24 +02:00
|
|
|
while (key_empty())
|
|
|
|
{
|
|
|
|
uint32_t now = sys_current_ms();
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
if (timers[i].enabled && int(timers[i].deadline - now) < 0)
|
|
|
|
return;
|
2022-10-13 22:36:25 +02:00
|
|
|
QThread::msleep(20);
|
2022-10-15 10:44:24 +02:00
|
|
|
}
|
2022-10-18 08:45:23 +02:00
|
|
|
CLR_ST(STAT_SUSPENDED | STAT_OFF | STAT_PGM_END);
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2022-10-15 10:44:24 +02:00
|
|
|
|
2023-06-30 20:46:01 +02:00
|
|
|
void sys_critical_start()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void sys_critical_end()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-10-15 10:44:24 +02:00
|
|
|
void sys_timer_disable(int timer_ix)
|
|
|
|
{
|
|
|
|
timers[timer_ix].enabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sys_timer_start(int timer_ix, uint32_t ms_value)
|
|
|
|
{
|
|
|
|
uint32_t now = sys_current_ms();
|
|
|
|
uint32_t then = now + ms_value;
|
|
|
|
timers[timer_ix].deadline = then;
|
|
|
|
timers[timer_ix].enabled = true;
|
|
|
|
}
|
|
|
|
int sys_timer_active(int timer_ix)
|
|
|
|
{
|
|
|
|
return timers[timer_ix].enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sys_timer_timeout(int timer_ix)
|
|
|
|
{
|
|
|
|
uint32_t now = sys_current_ms();
|
|
|
|
if (timers[timer_ix].enabled)
|
|
|
|
return int(timers[timer_ix].deadline - now) < 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-10-12 23:44:22 +02:00
|
|
|
void wait_for_key_press()
|
|
|
|
{
|
2023-06-23 19:50:21 +02:00
|
|
|
wait_for_key_release(-1);
|
|
|
|
while (key_empty() || !key_pop())
|
|
|
|
sys_sleep();
|
2024-01-16 19:37:15 +01:00
|
|
|
keysync_done = keysync_sent;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
|
|
|
void wait_for_key_release(int tout)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "wait_for_key_release not implemented");
|
2023-06-23 19:50:21 +02:00
|
|
|
while (!key_empty() && key_pop())
|
2023-08-26 17:49:42 +02:00
|
|
|
sys_sleep();
|
2024-01-16 19:37:15 +01:00
|
|
|
keysync_done = keysync_sent;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
2023-06-23 20:26:34 +02:00
|
|
|
int file_selection_screen(const char *title,
|
|
|
|
const char *base_dir,
|
|
|
|
const char *ext,
|
|
|
|
file_sel_fn_t sel_fn,
|
|
|
|
int disp_new,
|
|
|
|
int overwrite_check,
|
|
|
|
void *data)
|
2022-10-13 22:36:25 +02:00
|
|
|
{
|
2023-06-23 22:53:37 +02:00
|
|
|
int ret = 0;
|
2023-06-23 20:26:34 +02:00
|
|
|
|
|
|
|
// Make things relative to the working directory
|
|
|
|
if (*base_dir == '/' || *base_dir == '\\')
|
|
|
|
base_dir++;
|
|
|
|
|
2023-06-23 22:53:37 +02:00
|
|
|
QString path;
|
|
|
|
bool done = false;
|
2023-06-23 20:26:34 +02:00
|
|
|
|
2023-06-23 22:53:37 +02:00
|
|
|
postToThread([&]{ // the functor captures parent and text by value
|
|
|
|
path =
|
|
|
|
disp_new
|
|
|
|
? QFileDialog::getSaveFileName(nullptr,
|
|
|
|
title,
|
|
|
|
base_dir,
|
|
|
|
QString("*") + QString(ext),
|
|
|
|
nullptr,
|
|
|
|
overwrite_check
|
|
|
|
? QFileDialog::Options()
|
|
|
|
: QFileDialog::DontConfirmOverwrite)
|
|
|
|
: QFileDialog::getOpenFileName(nullptr,
|
|
|
|
title,
|
|
|
|
base_dir,
|
|
|
|
QString("*") + QString(ext));
|
|
|
|
std::cout << "Selected path: " << path.toStdString() << "\n";
|
|
|
|
done = true;
|
|
|
|
});
|
2023-06-23 20:26:34 +02:00
|
|
|
|
2023-06-23 22:53:37 +02:00
|
|
|
while (!done)
|
|
|
|
sys_sleep();
|
2023-06-23 20:26:34 +02:00
|
|
|
|
2023-06-23 22:53:37 +02:00
|
|
|
std::cout << "Got path: " << path.toStdString() << "\n";
|
|
|
|
QFileInfo fi(path);
|
|
|
|
QString name = fi.fileName();
|
|
|
|
ret = sel_fn(path.toStdString().c_str(),
|
|
|
|
name.toStdString().c_str(),
|
|
|
|
data);
|
|
|
|
return ret;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int power_check_screen()
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp, "file_selection_screen not imlemented");
|
2022-10-13 22:36:25 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sys_disk_ok()
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sys_disk_write_enable(int val)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-10-14 00:09:37 +02:00
|
|
|
uint32_t sys_current_ms()
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
return (tv.tv_sec * 1000000 + tv.tv_usec) / 1000;
|
|
|
|
}
|
|
|
|
|
2022-10-13 22:36:25 +02:00
|
|
|
|
|
|
|
FRESULT f_open(FIL *fp, const TCHAR *path, BYTE mode)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "f_open not implemented");
|
2022-10-14 09:10:34 +02:00
|
|
|
return FR_NOT_ENABLED;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
FRESULT f_close(FIL *fp)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "f_close not implemented");
|
2022-10-14 09:10:34 +02:00
|
|
|
return FR_NOT_ENABLED;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
FRESULT f_read(FIL *fp, void *buff, UINT btr, UINT *br)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "f_read not implemented");
|
2022-10-14 09:10:34 +02:00
|
|
|
return FR_NOT_ENABLED;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
FRESULT f_write(FIL *fp, const void *buff, UINT btw, UINT *bw)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "f_write not implemented");
|
2022-10-14 09:10:34 +02:00
|
|
|
return FR_NOT_ENABLED;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
FRESULT f_lseek(FIL *fp, FSIZE_t ofs)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "f_lseek not implemented");
|
2022-10-14 09:10:34 +02:00
|
|
|
return FR_NOT_ENABLED;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
FRESULT f_rename(const TCHAR *path_old, const TCHAR *path_new)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "f_rename not implemented");
|
2022-10-14 09:10:34 +02:00
|
|
|
return FR_NOT_ENABLED;
|
2022-10-13 22:36:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
FRESULT f_unlink(const TCHAR *path)
|
|
|
|
{
|
2022-10-16 13:18:19 +02:00
|
|
|
record(dmcp_notyet, "f_unlink not implemented");
|
2022-10-14 09:10:34 +02:00
|
|
|
return FR_NOT_ENABLED;
|
2022-10-12 23:44:22 +02:00
|
|
|
}
|
2023-06-22 23:38:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
void disp_disk_info(const char *hdr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_reset_state_file(const char * str)
|
|
|
|
{
|
2023-06-23 22:53:37 +02:00
|
|
|
QSettings settings;
|
|
|
|
settings.setValue("state", str);
|
2023-07-21 01:31:18 +02:00
|
|
|
record(dmcp, "Setting saved state: %s", str);
|
2023-06-22 23:38:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *get_reset_state_file()
|
|
|
|
{
|
2023-06-23 22:53:37 +02:00
|
|
|
static char result[256];
|
|
|
|
QSettings settings;
|
|
|
|
QString file = settings.value("state").toString();
|
|
|
|
result[0] = 0;
|
|
|
|
if (!file.isNull())
|
|
|
|
strncpy(result, file.toStdString().c_str(), sizeof(result));
|
2023-07-21 01:31:18 +02:00
|
|
|
record(dmcp, "Saved state: %+s", result);
|
2023-06-23 22:53:37 +02:00
|
|
|
return result;
|
2023-06-23 18:31:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t reset_magic = 0;
|
|
|
|
void set_reset_magic(uint32_t value)
|
|
|
|
{
|
|
|
|
reset_magic = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sys_reset()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int is_menu_auto_off()
|
|
|
|
{
|
|
|
|
return false;
|
2023-06-22 23:38:09 +02:00
|
|
|
}
|
2023-07-28 21:56:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
void rtc_read(tm_t * tm, dt_t *dt)
|
|
|
|
{
|
|
|
|
time_t now;
|
|
|
|
time(&now);
|
|
|
|
|
|
|
|
struct tm utm;
|
|
|
|
localtime_r(&now, &utm);
|
|
|
|
|
|
|
|
dt->year = 1900 + utm.tm_year;
|
|
|
|
dt->month = utm.tm_mon;
|
|
|
|
dt->day = utm.tm_mday;
|
|
|
|
|
|
|
|
tm->hour = utm.tm_hour;
|
|
|
|
tm->min = utm.tm_min;
|
|
|
|
tm->sec = utm.tm_sec;
|
|
|
|
tm->csec = 0;
|
|
|
|
tm->dow = utm.tm_wday;
|
|
|
|
}
|
|
|
|
|
2024-02-23 18:54:04 +01:00
|
|
|
void rtc_write(tm_t * tm, dt_t *dt)
|
|
|
|
{
|
|
|
|
record(dmcp_error, "Writing RTC %u/%u/%u %u:%u:%u (ignored)",
|
|
|
|
dt->day, dt->month, dt->year,
|
|
|
|
tm->hour, tm->min, tm->sec);
|
|
|
|
}
|
|
|
|
|
2023-07-28 21:56:12 +02:00
|
|
|
|
|
|
|
cstring get_wday_shortcut(int day)
|
|
|
|
{
|
2023-12-01 02:08:35 +01:00
|
|
|
static cstring dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
2023-07-28 21:56:12 +02:00
|
|
|
return dow[day];
|
|
|
|
}
|
|
|
|
|
|
|
|
cstring get_month_shortcut(int month)
|
|
|
|
{
|
|
|
|
static cstring name[] =
|
|
|
|
{
|
|
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
|
|
};
|
|
|
|
return name[month];
|
|
|
|
}
|
files: Allow `STO` and `RCL` to write to filesystem
When the argument describing the variable is a text, interpret that as
a file path on the disk. By default, data is stored in in a directory
named `data`, which will be created if necessary. For example, you can
use `3 "foo.48s" STO` to store the value `3` in a file named
`data/foo.48s` on disk.
How the file is stored depends on the extension given to the file:
* For `.txt` files, the object is stored as text
* For `.48s` files, the object is stored as DB48X source code
* For `.48b` files, the object is stored in binary format
* For `.csv` files, the object is stored in comma-separated format.
The binary format used for `.48b` includes a 4-byte magic number
identifying a DB48X format, and a 4-byte checksum used to ensure
binary compatibility between the firmware and the disk format.
At least during early days of development (prior to 1.0), it is quite
unlikely that the binary format for one version of the firmware would
be readable or writable by another version. If you need to recover
data from another version, you need to install that version and save
the object again in `.48s` (text) format.
Fixes: #375
Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
2023-11-14 07:23:58 +01:00
|
|
|
|
|
|
|
|
|
|
|
int check_create_dir(const char * dir)
|
|
|
|
{
|
|
|
|
return mkdir(dir, 0777);
|
|
|
|
}
|