Volkov: добавить Sprinter Commander

Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места.

Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
This commit is contained in:
2026-09-10 10:45:30 +03:00
parent 05bcd8197e
commit 8e389c03f8
870 changed files with 21310 additions and 25 deletions
+49
View File
@@ -0,0 +1,49 @@
/* sc_app.h — состояние и главный цикл Sprinter Commander. */
#ifndef SC_APP_H
#define SC_APP_H
#include <stdint.h>
#include "sc_command.h"
#include "sc_copy.h"
#include "sc_panel.h"
#define SC_PANEL_COUNT 2u
#define SC_PANEL_VISIBLE 27u
#define SC_EMM_PAGE_COUNT 4u
typedef struct ScApp {
ScPanel panels[SC_PANEL_COUNT];
uint8_t active_panel;
uint8_t running;
uint8_t emm_block;
uint8_t screen_page;
uint8_t copy_page;
uint16_t emm_total_before;
uint16_t emm_free_before;
ScCopyJob copy_job;
const char *status;
} ScApp;
int sc_app_init(ScApp *app);
void sc_app_run(ScApp *app);
int sc_app_cleanup(ScApp *app);
void sc_draw_all(const ScApp *app) __banked;
void sc_draw_panel_rows(const ScApp *app, uint8_t panel_index) __banked;
void sc_draw_panel_row(const ScApp *app, uint8_t panel_index,
uint8_t row) __banked;
void sc_draw_info(const ScApp *app) __banked;
void sc_draw_status(const ScApp *app) __banked;
uint8_t sc_dialog_confirm_quit(void) __banked;
uint8_t sc_dialog_confirm(const char *title, const char *message) __banked;
void sc_dialog_message(const char *title, const char *message) __banked;
uint8_t sc_dialog_choice(const char *title, const char *message,
const char *keys, const char *buttons) __banked;
uint8_t sc_dialog_input(const char *title, const char *prompt,
char *buffer, uint8_t capacity) __banked;
/* Возвращает выбранную ASCII-клавишу из keys в верхнем регистре; Esc = 0. */
uint8_t sc_dialog_choice(const char *title, const char *message,
const char *keys, const char *buttons) __banked;
#endif
+43
View File
@@ -0,0 +1,43 @@
/* sc_command.h — логические команды Commander. */
#ifndef SC_COMMAND_H
#define SC_COMMAND_H
#include "sc_input.h"
typedef enum {
SC_CMD_NONE = 0,
SC_CMD_CURSOR_UP,
SC_CMD_CURSOR_DOWN,
SC_CMD_PAGE_UP,
SC_CMD_PAGE_DOWN,
SC_CMD_HOME,
SC_CMD_END,
SC_CMD_PANEL_SWITCH,
SC_CMD_OPEN,
SC_CMD_PARENT,
SC_CMD_REFRESH,
SC_CMD_SORT_NAME,
SC_CMD_SORT_EXTENSION,
SC_CMD_SORT_DATE,
SC_CMD_SORT_SIZE,
SC_CMD_SELECT,
SC_CMD_INVERT_SELECTION,
SC_CMD_SELECT_MASK,
SC_CMD_UNSELECT_MASK,
SC_CMD_HELP,
SC_CMD_USER_MENU,
SC_CMD_VIEW,
SC_CMD_EDIT,
SC_CMD_COPY,
SC_CMD_MOVE_RENAME,
SC_CMD_MKDIR,
SC_CMD_DELETE,
SC_CMD_MAIN_MENU,
SC_CMD_QUIT,
SC_CMD_CANCEL
} ScCommand;
ScCommand sc_command_from_event(const ScKeyEvent *event);
#endif
+59
View File
@@ -0,0 +1,59 @@
/* sc_copy.h — состояние безопасного однофайлового copy-job PoC. */
#ifndef SC_COPY_H
#define SC_COPY_H
#include <stdint.h>
#define SC_COPY_PATH_CAPACITY 256u
#define SC_COPY_CHUNK_SIZE 4096u
#define SC_COPY_IDLE 0u
#define SC_COPY_RUNNING 1u
#define SC_COPY_DONE 2u
#define SC_COPY_CANCELLED 3u
#define SC_COPY_ERROR 4u
#define SC_COPY_STEP_ERROR (-1)
#define SC_COPY_STEP_DONE 0
#define SC_COPY_STEP_MORE 1 /* нужен ещё шаг; done==total означает pre-commit */
typedef struct {
char source_path[SC_COPY_PATH_CAPACITY];
char target_path[SC_COPY_PATH_CAPACITY];
char temp_path[SC_COPY_PATH_CAPACITY];
uint32_t total;
uint32_t done;
uint16_t source_date;
uint16_t source_time;
int source_fd;
int temp_fd;
int error;
uint8_t page;
uint8_t state;
uint8_t temp_exists;
uint8_t source_attr;
uint8_t existing_attr;
uint8_t preserve_metadata;
uint8_t replace_existing;
uint8_t committed;
} ScCopyJob;
int sc_copy_begin(ScCopyJob *job, const char *source_dir,
const char *target_dir, const char *name,
uint32_t size, uint8_t page) __banked;
/* source_name и target_name могут различаться; replace включает backup/rollback. */
int sc_copy_begin_policy(ScCopyJob *job, const char *source_dir,
const char *target_dir, const char *source_name,
const char *target_name, uint32_t size,
uint8_t page, uint8_t replace) __banked;
/* Для tree-job: CWD уже равен source-dir, а target_path подготовлен целиком. */
int sc_copy_begin_prepared(ScCopyJob *job, const char *name,
uint32_t size, uint8_t page) __banked;
int sc_copy_begin_prepared_policy(ScCopyJob *job, const char *name,
uint32_t size, uint8_t page,
uint8_t replace) __banked;
int sc_copy_step(ScCopyJob *job) __banked;
void sc_copy_cancel(ScCopyJob *job) __banked;
#endif
+10
View File
@@ -0,0 +1,10 @@
/* sc_copy_ui.h — банковый обработчик команды F5 и её прогресса. */
#ifndef SC_COPY_UI_H
#define SC_COPY_UI_H
struct ScApp;
void sc_copy_selected(struct ScApp *app) __banked;
#endif
+11
View File
@@ -0,0 +1,11 @@
/* sc_disk.h — проверка свободных кластеров целевого диска через DSKINFO. */
#ifndef SC_DISK_H
#define SC_DISK_H
#include <stdint.h>
/* 1 — места достаточно; 0 — недостаточно; -1 — ошибка DSS. */
int sc_disk_has_space(const char *path, uint32_t bytes) __banked;
#endif
+27
View File
@@ -0,0 +1,27 @@
/* sc_entry.h — компактная 24-байтовая запись каталога в EMM. */
#ifndef SC_ENTRY_H
#define SC_ENTRY_H
#include <stdint.h>
#define SC_ENTRY_PARENT 0x01u
#define SC_ENTRY_SELECTED 0x02u
#define SC_ENTRY_DSS_LIMIT 0x04u
#define SC_DIR_CAPACITY 640u
typedef struct {
char name[13];
uint32_t size;
uint16_t date;
uint16_t time;
uint8_t attr;
uint8_t flags;
uint8_t reserved;
} ScDirEntry;
/* SDCC обязан сохранить зафиксированный дисковый формат без padding. */
typedef char ScDirEntry_must_be_24_bytes[(sizeof(ScDirEntry) == 24u) ? 1 : -1];
#endif
+8
View File
@@ -0,0 +1,8 @@
/* sc_exec.h — низкоуровневый запуск EXE через ESTEX. */
#ifndef SC_EXEC_H
#define SC_EXEC_H
int sc_platform_exec(const char *path);
#endif
+10
View File
@@ -0,0 +1,10 @@
/* sc_exec_ui.h — банковый обработчик Enter на EXE. */
#ifndef SC_EXEC_UI_H
#define SC_EXEC_UI_H
struct ScApp;
void sc_exec_selected(struct ScApp *app) __banked;
#endif
+17
View File
@@ -0,0 +1,17 @@
/* sc_file_ops.h — низкоуровневые безопасные файловые операции Commander. */
#ifndef SC_FILE_OPS_H
#define SC_FILE_OPS_H
#include <stdint.h>
/* Переименовать old_name в new_name внутри directory и восстановить CWD.
* 0 — успех; -1 — операция отклонена, CWD восстановлен;
* -2 — восстановить CWD после попытки не удалось. */
int sc_rename_local(const char *directory,
const char *old_name,
const char *new_name,
char *cwd_buffer,
uint16_t cwd_capacity) __banked;
#endif
+12
View File
@@ -0,0 +1,12 @@
/* sc_file_ui.h — банковые UI-команды безопасных файловых операций. */
#ifndef SC_FILE_UI_H
#define SC_FILE_UI_H
#include "sc_app.h"
void sc_mkdir_command(ScApp *app) __banked;
void sc_rename_command(ScApp *app) __banked;
void sc_delete_command(ScApp *app) __banked;
#endif
+40
View File
@@ -0,0 +1,40 @@
/*
* sc_glyphs.h — raw-коды CP866/CP437 для интерфейса Commander.
*
* Управляющие байты 0x18..0x1B здесь являются экранными глифами. Их нельзя
* передавать функциям, интерпретирующим CR/LF/BS/TAB.
*/
#ifndef SC_GLYPHS_H
#define SC_GLYPHS_H
#define SC_G_H 0xC4u
#define SC_G_V 0xB3u
#define SC_G_TL 0xDAu
#define SC_G_TR 0xBFu
#define SC_G_BL 0xC0u
#define SC_G_BR 0xD9u
#define SC_G_TM 0xC2u
#define SC_G_BM 0xC1u
#define SC_G_ML 0xC3u
#define SC_G_MR 0xB4u
#define SC_G_X 0xC5u
#define SC_G_ARROW_UP 0x18u
#define SC_G_ARROW_DOWN 0x19u
#define SC_G_ARROW_RIGHT 0x1Au
#define SC_G_ARROW_LEFT 0x1Bu
#define SC_G_SHADE_LIGHT 0xB0u
#define SC_G_SHADE_MEDIUM 0xB1u
#define SC_G_SHADE_DARK 0xB2u
#define SC_G_BLOCK 0xDBu
#define SC_G_HALF_UP 0xDFu
#define SC_G_HALF_DOWN 0xDCu
#define SC_G_HALF_LEFT 0xDDu
#define SC_G_HALF_RIGHT 0xDEu
#define SC_G_BULLET 0xF9u
#define SC_G_CHECK 0xFBu
#define SC_G_SQUARE 0xFEu
#endif
+21
View File
@@ -0,0 +1,21 @@
/* sc_input.h — нормализованное событие клавиатуры. */
#ifndef SC_INPUT_H
#define SC_INPUT_H
#include <stdint.h>
#define SC_MOD_SHIFT 0x01u
#define SC_MOD_CTRL 0x02u
#define SC_MOD_ALT 0x04u
typedef struct {
uint8_t ascii;
uint8_t scan;
uint8_t modifiers;
} ScKeyEvent;
void sc_input_read(ScKeyEvent *event);
uint8_t sc_input_poll(ScKeyEvent *event);
#endif
+13
View File
@@ -0,0 +1,13 @@
/* sc_metadata.h — узкие wrappers записи файловых метаданных DSS. */
#ifndef SC_METADATA_H
#define SC_METADATA_H
#include <stdint.h>
int sc_metadata_set_datetime(uint8_t fd, uint16_t date,
uint16_t time) __banked;
int sc_metadata_get_attributes(const char *path, uint8_t *attr) __banked;
int sc_metadata_set_attributes(const char *path, uint8_t attr) __banked;
#endif
+41
View File
@@ -0,0 +1,41 @@
/* sc_panel.h — состояние одной независимой файловой панели. */
#ifndef SC_PANEL_H
#define SC_PANEL_H
#include <stdint.h>
#include "sc_entry.h"
#define SC_SOURCE_REAL_FS 0u
typedef enum {
SC_SORT_NAME = 0,
SC_SORT_EXTENSION,
SC_SORT_SIZE,
SC_SORT_DATE
} ScSortKey;
typedef struct {
char path[256];
uint16_t count;
uint16_t cursor;
uint16_t top;
uint32_t total_size;
uint16_t selected_count;
uint32_t selected_size;
uint8_t source_kind;
uint8_t phys_page;
uint8_t truncated;
uint8_t dirty;
uint8_t sort_key;
uint8_t sort_descending;
} ScPanel;
int sc_panel_init(ScPanel *panel, uint8_t phys_page);
int sc_panel_refresh(ScPanel *panel);
int sc_panel_selected(const ScPanel *panel, ScDirEntry *entry);
int sc_panel_open_selected(ScPanel *panel);
int sc_panel_parent(ScPanel *panel);
void sc_panel_normalize_view(ScPanel *panel);
#endif
+12
View File
@@ -0,0 +1,12 @@
/* sc_path.h — безопасные операции над путями DOS 8.3 Commander. */
#ifndef SC_PATH_H
#define SC_PATH_H
#include <stdint.h>
int sc_path_join(char *out, uint16_t capacity,
const char *directory, const char *name);
int sc_path_validate_component(const char *name) __banked;
#endif
+38
View File
@@ -0,0 +1,38 @@
/* sc_screen.h — экранная EMM-модель Commander 80x32. */
#ifndef SC_SCREEN_API_H
#define SC_SCREEN_API_H
#include <stdint.h>
#define SC_SCREEN_W 80u
#define SC_SCREEN_H 32u
#define SC_ROW_BYTES 160u
#define SC_SCREEN_BYTES 5120u
#define SC_SCROLL_UP 1u
#define SC_SCROLL_DOWN 2u
typedef struct {
uint8_t ch;
uint8_t attr;
} ScCell;
typedef char ScCell_must_be_2_bytes[(sizeof(ScCell) == 2) ? 1 : -1];
void sc_screen_attach(uint8_t page);
void sc_screen_detach(void);
void sc_screen_clear(uint8_t attr);
void sc_screen_cell(uint8_t x, uint8_t y, uint8_t ch, uint8_t attr);
void sc_screen_fill(uint8_t x, uint8_t y, uint8_t width,
uint8_t ch, uint8_t attr);
void sc_screen_text(uint8_t x, uint8_t y, const char *text,
uint8_t attr, uint8_t max_width);
int sc_screen_present_full(void);
int sc_screen_present_row(uint8_t x, uint8_t y, uint8_t width);
int sc_screen_present_rows(uint8_t first, uint8_t count);
int sc_screen_scroll_rows(uint8_t x, uint8_t y,
uint8_t width, uint8_t height,
uint8_t direction, uint8_t fill_attr);
#endif
+16
View File
@@ -0,0 +1,16 @@
/* sc_select.h — выбор записей текущей панели для групповых операций. */
#ifndef SC_SELECT_H
#define SC_SELECT_H
#include "sc_panel.h"
#define SC_SELECT_TOGGLE 0u
#define SC_SELECT_INVERT 1u
#define SC_SELECT_MASK_SET 2u
#define SC_SELECT_MASK_CLEAR 3u
int sc_select_apply(ScPanel *panel, uint8_t action,
const char *mask) __banked;
#endif
+11
View File
@@ -0,0 +1,11 @@
/* sc_sort.h — сортировка EMM-записей панели. */
#ifndef SC_SORT_H
#define SC_SORT_H
#include "sc_panel.h"
int sc_sort_panel(ScPanel *panel) __banked;
int sc_sort_set(ScPanel *panel, uint8_t key) __banked;
#endif
+11
View File
@@ -0,0 +1,11 @@
/* sc_source.h — источник настоящего каталога DSS для P3. */
#ifndef SC_SOURCE_H
#define SC_SOURCE_H
#include "sc_panel.h"
int sc_source_fs_scan(ScPanel *panel) __banked;
int sc_source_fs_change_dir(ScPanel *panel, const char *name) __banked;
#endif
+14
View File
@@ -0,0 +1,14 @@
/* sc_store.h — доступ к записям панели без публикации W3-указателей. */
#ifndef SC_STORE_H
#define SC_STORE_H
#include <stdint.h>
#include "sc_panel.h"
int sc_store_get(const ScPanel *panel, uint16_t index, ScDirEntry *out);
int sc_store_put(const ScPanel *panel, uint16_t index,
const ScDirEntry *entry);
void sc_store_clear(ScPanel *panel);
#endif
+31
View File
@@ -0,0 +1,31 @@
/*
* sc_theme.h — смысловые атрибуты интерфейса.
*
* P2.2 подтвердил системный save/set/restore четырёх текстовых палитровых
* планов. Renderer знает только эти роли, а точные RGB принадлежат модулю
* sc_theme.c.
*/
#ifndef SC_THEME_H
#define SC_THEME_H
#include <conio.h>
#define SC_ATTR_FRAME COLOR(COLOR_WHITE, COLOR_BLUE)
#define SC_ATTR_TITLE COLOR(COLOR_YELLOW, COLOR_BLUE)
#define SC_ATTR_NORMAL COLOR(COLOR_LIGHTCYAN, COLOR_BLUE)
#define SC_ATTR_FILE COLOR(COLOR_LIGHTGRAY, COLOR_BLUE)
#define SC_ATTR_DIR SC_ATTR_FRAME
#define SC_ATTR_EXE SC_ATTR_TITLE
#define SC_ATTR_ACTIVE COLOR(COLOR_BLACK, COLOR_CYAN)
#define SC_ATTR_STATUS COLOR(COLOR_BLACK, COLOR_LIGHTGRAY)
#define SC_ATTR_DIALOG COLOR(COLOR_WHITE, COLOR_RED)
#define SC_ATTR_LIMIT COLOR(COLOR_LIGHTRED, COLOR_BLUE)
#define SC_ATTR_LIMIT_ACTIVE COLOR(COLOR_LIGHTRED, COLOR_CYAN)
#define SC_ATTR_KEYS SC_ATTR_ACTIVE
int sc_theme_init(void);
void sc_theme_reapply(void);
void sc_theme_shutdown(void);
#endif
+43
View File
@@ -0,0 +1,43 @@
/* sc_tree.h — пошаговый BFS-обход дерева для рекурсивных файловых jobs. */
#ifndef SC_TREE_H
#define SC_TREE_H
#include <stdint.h>
#include "sc_copy.h"
#include "sc_entry.h"
#define SC_TREE_STEP_ERROR (-1)
#define SC_TREE_STEP_DONE 0
#define SC_TREE_STEP_FILE 1
#define SC_TREE_STEP_DIRECTORY 2
#define SC_TREE_STEP_SCAN_DIR 3
typedef struct {
uint8_t block;
uint8_t page;
uint8_t active;
uint8_t scan_state;
uint16_t head;
uint16_t tail;
uint16_t current;
uint16_t reverse;
uint16_t created_directories;
uint16_t files_seen;
int error;
} ScTreeJob;
int sc_tree_begin(ScTreeJob *job, ScCopyJob *copy) __banked;
int sc_tree_add_root(ScTreeJob *job, ScCopyJob *copy,
const char *source_base, const char *target_base,
const char *name) __banked;
int sc_tree_add_delete_root(ScTreeJob *job, const char *name) __banked;
int sc_tree_next(ScTreeJob *job, ScCopyJob *copy,
const char *source_base, const char *target_base,
ScDirEntry *entry) __banked;
int sc_tree_delete_next(ScTreeJob *job, ScCopyJob *copy,
const char *source_base,
ScDirEntry *entry) __banked;
int sc_tree_finish(ScTreeJob *job, ScCopyJob *copy) __banked;
#endif
+22
View File
@@ -0,0 +1,22 @@
/* sc_video.h — системный координатный вывод без BIOS window descriptors. */
#ifndef SC_VIDEO_H
#define SC_VIDEO_H
#include <stdint.h>
int sc_video_init(void);
int sc_video_restore(void);
void sc_video_shutdown(void);
int sc_video_present_rect(uint8_t page, uint16_t offset,
uint8_t row, uint8_t col,
uint8_t height, uint8_t width);
int sc_video_scroll_rect(uint8_t row, uint8_t col,
uint8_t height, uint8_t width,
uint8_t direction);
void sc_platform_winrest(uint8_t row, uint8_t col,
uint8_t height, uint8_t width,
uint8_t page, uint16_t offset);
#endif