ChangeLog:
- big commit.
This commit is contained in:
@@ -24,6 +24,9 @@ examples/*/*.cdb
|
|||||||
examples/*/*.mem
|
examples/*/*.mem
|
||||||
examples/*/*.rst
|
examples/*/*.rst
|
||||||
|
|
||||||
|
# Temporary build directory for floppy disk image preparation
|
||||||
|
examples/*/.disk_tmp/
|
||||||
|
|
||||||
tests/*/*.exe
|
tests/*/*.exe
|
||||||
tests/*/*.asm
|
tests/*/*.asm
|
||||||
tests/*/*.lst
|
tests/*/*.lst
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pages by program size — small programs get only one page. Pick a memory mode
|
|||||||
on what your program needs:
|
on what your program needs:
|
||||||
|
|
||||||
| Mode | Code lives in | Banking | Use when | Note |
|
| Mode | Code lives in | Banking | Use when | Note |
|
||||||
|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | |
|
| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | |
|
||||||
| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | |
|
| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | |
|
||||||
| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | |
|
| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | |
|
||||||
|
|||||||
@@ -1,10 +1,39 @@
|
|||||||
# Build mdview.exe — Markdown viewer for Sprinter.
|
# Build mdview.exe — Markdown viewer for Sprinter.
|
||||||
#
|
#
|
||||||
# small memory mode: code in W1, data/stack/heap in W2 (32 KB total).
|
# small memory mode: code in W1, data/stack/heap in W2 (32 KB total).
|
||||||
# W3 stays free for the file buffer (EMM-mapped) and Phase 3+ render cache.
|
# W3 stays free for the file buffer (EMM-mapped).
|
||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := mdview
|
EXAMPLE := mdview
|
||||||
MEMORY := small
|
MEMORY := small
|
||||||
EXTRA_DATA := SAMPLE.MD PLAN_866.md SAMPLEF.MD
|
|
||||||
include $(PROJ_ROOT)/app.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Образ дискеты: только mdview.exe + README.MD (перекодированный
|
||||||
|
# из UTF-8 в CP866 — рабочую кодировку Sprinter).
|
||||||
|
#
|
||||||
|
# README.MD хранится в репозитории в UTF-8; iconv -c конвертирует
|
||||||
|
# его в CP866, отбрасывая символы без аналога в целевой кодировке.
|
||||||
|
# Результат кладётся в .disk_tmp/README.MD, чтобы make_disk.py
|
||||||
|
# использовал правильное имя файла на диске.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
DISK_TMP := .disk_tmp
|
||||||
|
README_DISK := $(DISK_TMP)/README.MD
|
||||||
|
|
||||||
|
$(DISK_TMP):
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
$(README_DISK): README.MD | $(DISK_TMP)
|
||||||
|
iconv -c -f UTF-8 -t CP866 README.MD > $@
|
||||||
|
|
||||||
|
floppy: $(EXAMPLE).exe $(README_DISK)
|
||||||
|
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(README_DISK)
|
||||||
|
@echo
|
||||||
|
@echo "Floppy ready: $(FLOPPY_IMG)"
|
||||||
|
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf .sprinter-cc-* $(EXAMPLE).exe $(DISK_TMP)
|
||||||
|
|
||||||
|
.PHONY: all clean floppy run
|
||||||
|
|||||||
@@ -1,570 +0,0 @@
|
|||||||
# План: текстовый Markdown viewer для Sprinter (`examples/mdview`)
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Тестовая крупная задача — проверить нашу libc на нетривиальном interactive-приложении (полноэкранный UI, файловый I/O, парсер). Параллельно даст хороший showcase платформы и поможет вытащить недоделки в conio/io. Конечная цель: viewer для `.md` файлов с подсветкой синтаксиса, навигацией по тексту и постраничным скроллингом.
|
|
||||||
|
|
||||||
**Ограничения v1 (зафиксированы пользователем):**
|
|
||||||
- POSIX file API (open/read/lseek/close); FILE pointer / fread / fgets — не использовать
|
|
||||||
- Подсветка через цвет: **размер заголовка** → цвет шрифта; **bold/italic** → цвет фона (моноширинный фонт без жирного/курсивного начертания)
|
|
||||||
|
|
||||||
**Расширения, реализованные после плана:**
|
|
||||||
- Файлы до 128 KB (1–8 EMM-страниц, lazy map в W3 через `fb()/map_page()`) — изначально было в v2
|
|
||||||
- Анимированный spinner и предварительная отрисовка UI при старте — UX
|
|
||||||
- Inline emphasis: `_`/`*`/`**` подчиняются XOR flanking-правилу (whitespace ровно с одной стороны), `COLOR_YELLOW` и `2 * 3` остаются литералами
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Текущий статус (2026-06-05)
|
|
||||||
|
|
||||||
| Phase | Статус | Комментарий |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 Plain text + nav | ✓ | загрузка, индексация, status/menu, ↑↓/PgUp/PgDn/Home/End/F1/F10/Esc |
|
|
||||||
| 2 Headers + HR | ✓ | H1..H4, `---`/`***`/`___` (с ≥3 marker'ов) |
|
|
||||||
| 3 Inline emphasis | ✓ | `**` / `*` / `_` / `` ` ``; XOR flanking (см. выше) |
|
|
||||||
| 4 Lists / quote / fenced code | ✓ | `- / * / +`, `N. / N)`, `> `, ``` ``` ```; light nested lists |
|
|
||||||
| 4-tables | ✗ | таблицы отложены вместе с Phase 6 |
|
|
||||||
| 5 Wrap / Unwrap (F2) | ✓ | wrap-by-default; soft wrap; F2 переключает; hpan ←/→ в truncate-режиме |
|
|
||||||
| 6 Полный layout таблиц | ✗ | deferred |
|
|
||||||
| 7 Links + search | ✗ | deferred |
|
|
||||||
| 8 F8 Raw toggle | ✗ | deferred |
|
|
||||||
| Cache рендеренных строк | ✗ | не нужно по скорости |
|
|
||||||
|
|
||||||
**UX-поправки (отдельно от phase-плана, 2026-06-05):**
|
|
||||||
- UI (menu + title bar) отрисовывается ДО `load_file`/`index_lines` — пользователь сразу видит интерфейс, а не чёрный экран
|
|
||||||
- Title bar: `MDVIEW <spinner> <filename>` (3 пробела между MDVIEW и filename, slot спиннера — col 8)
|
|
||||||
- Spinner крутится во время `load_file` (по странице) и `index_lines` (раз в 32 логических строки), включая `toggle_wrap`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Архитектура
|
|
||||||
|
|
||||||
### Раскладка экрана (80×32, текст mode 0x03)
|
|
||||||
|
|
||||||
```
|
|
||||||
Row 0: ┃ MDVIEW │ mdview.md │ L 1-30 / 142 │ 21% ┃ ← status bar (BG=blue, FG=white)
|
|
||||||
▲ ▲
|
|
||||||
│ └── filename @ col 10
|
|
||||||
└────────── spinner slot @ col 8 (anim. while busy)
|
|
||||||
Row 1: ┃ ┃
|
|
||||||
... ┃ document viewport (30 rows) ┃
|
|
||||||
Row 30: ┃ ┃
|
|
||||||
Row 31: ┃ F1 Help F2 Wrap F10 Exit ┃ ← menu bar (BG=blue, FG=cyan)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Viewport = 30 строк × 80 столбцов.
|
|
||||||
- Status / menu рендерятся через `wrchar()` (без авто-скролла), viewport — через `LOCATE` + посимвольный `wrchar()` (тоже без авто-скролла, ставит и char и attr за один call).
|
|
||||||
|
|
||||||
### Память
|
|
||||||
|
|
||||||
Memory mode: **`small`** — DSS отводит под наш образ **два банка (W1 + W2)** = 32 KB суммарно (CODE в W1, DATA + STACK + HEAP в W2). Этого должно хватить чтобы НЕ заводить `__banked` функции. W3 остаётся полностью свободным для маппинга больших буферов:
|
|
||||||
|
|
||||||
```
|
|
||||||
W0 (0x0000-0x3FFF): ESTEX (system, untouchable)
|
|
||||||
W1 (0x4000-0x7FFF): CODE (small mode page 1)
|
|
||||||
W2 (0x8000-0xBFFF): DATA + STACK + HEAP (small mode page 2)
|
|
||||||
W3 (0xC000-0xFFFF): paged window — лениво переключается между EMM-страницами
|
|
||||||
файла (до 8 страниц = 128 KB). `cur_page` кэширует
|
|
||||||
текущую mapping, `fb(p)` маппит нужную страницу при
|
|
||||||
первом обращении.
|
|
||||||
```
|
|
||||||
|
|
||||||
Почему small + W3:
|
|
||||||
- 32 KB на код+данные с большим запасом → нет банкинга
|
|
||||||
- W3 — стандартный paged window, под него у нас уже есть `bank_io_w3` API
|
|
||||||
- Файлы до 128 KB поддерживаются нативно: `mem_alloc_pages(pages_needed)` под весь файл; `map_page()` через `sprinter_page_w3()`; `fb(p)` — единая точка доступа из индексатора и рендера.
|
|
||||||
|
|
||||||
Статики (в W2):
|
|
||||||
- `line_offset[MAX_LINES]` — uint16_t смещение каждой строки в файловой странице (4 KB на 2048 строк)
|
|
||||||
- `cache_tag[CACHE_N]` — uint16_t тег слота (200 байт на 100 слотов, появляется в Phase 3)
|
|
||||||
- `filename[64]`, `top_line`, `total_lines`, `file_size`, `file_blk`, `cache_blk` — единицы байт
|
|
||||||
|
|
||||||
**FILE_BUF**: `((char*)0xC000)` — фиксированная адресация в W3 после маппинга нужной страницы.
|
|
||||||
|
|
||||||
### Поток данных
|
|
||||||
|
|
||||||
```
|
|
||||||
main → open() → read() chunks 1KB → write to W1 (mapped EMM page) → close()
|
|
||||||
→ index_lines() (одно сканирование, заполняет line_offset[])
|
|
||||||
→ render_viewport() + main loop { getkey(); handle(); render_status(); render_viewport() }
|
|
||||||
```
|
|
||||||
|
|
||||||
**Тонкость с `read()`**: ESTEX READ записывает по dst-указателю в адресном пространстве вызывающего. Поскольку мы маппим EMM-страницу в W3 (0xC000) ДО вызова read(), указатель 0xC000+offset валиден. Если выяснится что BIOS трогает W3 во время read (графический видеобуфер по умолчанию в W3 при графических режимах, но в текстовом — должен быть свободен) — fallback: читать в 1 KB буфер в W2 и копировать в W3 через `bank_write_w3()`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Реализация — поэтапная
|
|
||||||
|
|
||||||
### Phase 1 — Plain text viewer (MVP) ✓
|
|
||||||
|
|
||||||
**Что работает:**
|
|
||||||
- Загрузка файла (`open/read/close`) в W1-страницу
|
|
||||||
- Индексация строк (LF / CRLF разделители)
|
|
||||||
- Status bar: имя файла, L N-M / Total, процент скроллинга
|
|
||||||
- Menu bar: `F1 Help F10 Exit`
|
|
||||||
- Навигация: ↑/↓ (1 строка), PgUp/PgDn (30 строк), Home/End (начало/конец), Esc/F10 (выход), F1 (help screen)
|
|
||||||
- Обрезка строк длиннее 80 символов (без word-wrap)
|
|
||||||
- Цвета: текст белый на чёрном; status/menu — белый на синем
|
|
||||||
|
|
||||||
**Критичные файлы:**
|
|
||||||
- `examples/mdview/mdview.c` — main, key loop, rendering, indexing (one-file MVP)
|
|
||||||
- `examples/mdview/Makefile`
|
|
||||||
- `examples/mdview/SAMPLE.MD` — тестовый markdown файл
|
|
||||||
|
|
||||||
### Phase 2 — Headers и горизонтальная линия ✓
|
|
||||||
|
|
||||||
**MD фичи:**
|
|
||||||
- `# H1` → ярко-жёлтый (COLOR_YELLOW = 14) на чёрном
|
|
||||||
- `## H2` → ярко-голубой (COLOR_LBLUE = 11)
|
|
||||||
- `### H3` → ярко-зелёный (COLOR_LGREEN = 10)
|
|
||||||
- `#### H4+` → серый (COLOR_GREY = 8)
|
|
||||||
- `---` / `***` на отдельной строке → линия 0xC4 (горизонтальная рамка ASCII) во всю ширину
|
|
||||||
|
|
||||||
### Phase 3 — Inline emphasis ✓
|
|
||||||
|
|
||||||
**Парсер inline (per-line, runs в одну строку):**
|
|
||||||
- `**bold**` → ATTR_TEXT_BOLD
|
|
||||||
- `*italic*` → ATTR_TEXT_ITALIC
|
|
||||||
- `_underscore_` → ATTR_TEXT_UNDERSORE
|
|
||||||
- `` `code` `` → ATTR_TEXT_CODE
|
|
||||||
- Маркеры `**`/`*`/`_`/`` ` `` НЕ рендерятся (съедаются)
|
|
||||||
|
|
||||||
State machine: один активный стиль одновременно (без вложенности); конфликтующий маркер при чужом активном стиле всё равно консьюмится (zero-width) для синхронизации ширины с index_lines. Состояние сбрасывается на каждой строке.
|
|
||||||
|
|
||||||
**Flanking-правило (CommonMark intraword, реализовано после изначального плана):**
|
|
||||||
- `*` / `**` / `_` считаются markdown-маркером только если whitespace/EOL
|
|
||||||
ровно с ОДНОЙ стороны (XOR).
|
|
||||||
- Случаи "оба whitespace" (`2 * 3`, `2 ** 3`) → литералы (арифметика).
|
|
||||||
- Случаи "ни одного whitespace" (`COLOR_YELLOW`, `FILE*/fread`, `foo*bar*baz`)
|
|
||||||
→ литералы (intraword).
|
|
||||||
- Backtick (`` ` ``) flanking НЕ требует — `` `code` `` работает без пробелов.
|
|
||||||
- Правило применено симметрично в 4 местах (`index_lines`, cont-render,
|
|
||||||
основной inline-парсер, truncation peek), иначе wrap-индексатор и
|
|
||||||
рендер разъедутся по ширине.
|
|
||||||
|
|
||||||
> **Кэш отформатированных строк** — отложен в самый конец, см. "Phase ∞: оптимизации".
|
|
||||||
> Скорости текущего наивного рендера хватает на 80×30 = 2400 wrchar / кадр; PgUp/PgDn визуально мгновенен.
|
|
||||||
|
|
||||||
### Phase 4 — Block elements ✓ (без таблиц)
|
|
||||||
|
|
||||||
- Маркированные списки: `- foo`, `* foo`, `+ foo` → префикс `•` (0x07) + пробел; цвет маркера ярче основного
|
|
||||||
- Нумерованные списки: `1. foo`, `2. foo` → как есть (число оставляем)
|
|
||||||
- Blockquote: строки с `> ` → префикс `│` (0xB3) серого цвета, остальной текст слегка приглушённый
|
|
||||||
- Fenced code blocks: `` ``` `` открывает/закрывает блок; все строки между — bg=серый, моноширинно (без inline-парсинга)
|
|
||||||
- Indented code blocks (4+ пробелов): аналогично fenced, но без явного маркера
|
|
||||||
|
|
||||||
**Light nested lists (v1 — реализовано):**
|
|
||||||
- `classify_line()` пропускает leading spaces перед ulist/olist/quote маркером,
|
|
||||||
возвращает `content_off` после маркера → `content_off - p_start` = indent + marker
|
|
||||||
бит в visible col.
|
|
||||||
- `render_line()` рисует leading-spaces в `ATTR_TEXT`, потом маркер на сдвинутой
|
|
||||||
позиции (col = indent). Marker всё ещё фиксирован при горизонтальном pan'е.
|
|
||||||
- HR / header / fence delim остаются строго col-0 (CommonMark разрешает до 3
|
|
||||||
ведущих пробелов для них — упростили).
|
|
||||||
- Tab-indent → не распознаётся как nesting (только spaces).
|
|
||||||
|
|
||||||
**Phase 4-full — полная поддержка вложенности (deferred):**
|
|
||||||
- **Tab-indent**: считать tab = 4 пробела для определения уровня.
|
|
||||||
- **Quote nesting** (`> > foo`): каждый `>` подряд = +1 уровень, каждый рисуется
|
|
||||||
отдельным `│` в `ATTR_QUOTE_MARKER` (визуальная "лестница" слева).
|
|
||||||
- **Hanging indent в wrap-continuation**: когда `- some very long bullet text
|
|
||||||
that wraps...` — continuation seg должен начинаться от content-col (после
|
|
||||||
маркера), а не от col 0. Сейчас continuation идёт от col 0 (v1 simplification).
|
|
||||||
Требует хранить `marker_width` per логическая строка (8 бит) или re-classify
|
|
||||||
first seg при рендере continuation.
|
|
||||||
- **Lazy continuation**: строки без маркера, но с правильным indent под
|
|
||||||
предыдущим bullet'ом, должны считаться продолжением того bullet'а
|
|
||||||
(визуально — общий attr).
|
|
||||||
- **Strict CommonMark indent rules**: вложенный пункт должен быть на indent
|
|
||||||
≥ content_col родителя, иначе считается breakout. Нужен мини-stack
|
|
||||||
активных списков при индексации.
|
|
||||||
|
|
||||||
### Phase 5 — Wrap / Unwrap длинных строк ✓
|
|
||||||
|
|
||||||
Дефолт: **wrap on**. F2 переключает; в меню-баре подпись отражает действие
|
|
||||||
("Unwrap" когда wrap включён, "Wrap" когда выключен). Во время реиндексации
|
|
||||||
крутится спиннер на title bar.
|
|
||||||
|
|
||||||
**v1 — реализовано:**
|
|
||||||
- Один массив `line_offset[2048]` хранит ВИДИМЫЕ сегменты (а не логические
|
|
||||||
строки); биты 0..13 — байтовое смещение, бит 15 — CONT-флаг continuation.
|
|
||||||
- Wrap-режим: soft wrap на последнем пробеле ≤ 80; hard fallback если
|
|
||||||
пробела нет.
|
|
||||||
- Маркеры эмфазиса (`**`/`*`/`_`/`` ` ``) и header-префиксы (`#`/`##`/...) не
|
|
||||||
учитываются в visible-col при поиске точки переноса.
|
|
||||||
- "Специальные" логические строки не wrap'аются вообще (одна seg-запись на
|
|
||||||
логическую строку): fence delim, table row (header/separator/body), HR.
|
|
||||||
- F2 toggle сохраняет визуальную позицию через `top_offset` в FILE_BUF.
|
|
||||||
- Bitmaps (`in_code`/`in_table`/`is_tab_hdr`) перестраиваются вместе с
|
|
||||||
сегментами, индексируются seg-индексом, биты ставятся только на первом
|
|
||||||
seg'е логической строки.
|
|
||||||
- Continuation-сегменты рендерятся в стиле "v1: плоско" — plain text,
|
|
||||||
никаких markdown-классификаций; padding до конца строки `ATTR_TEXT`.
|
|
||||||
|
|
||||||
**v1.5 — отложено для полной картины wrap:**
|
|
||||||
- **Hanging indent**: continuation от ulist/olist/quote должен выравниваться
|
|
||||||
под content, а не от col 0. Требует хранить marker_width per логическая
|
|
||||||
строка ИЛИ re-classify первого seg'а при рендере continuation.
|
|
||||||
- **Наследование base_attr**: continuation от header'а должен сохранять
|
|
||||||
цвет; continuation от code body — фон ATTR_TEXT_CODE. Требует хранить
|
|
||||||
1 байт `base_attr` per seg ИЛИ lookup первого seg'а.
|
|
||||||
- **Inline emphasis через границу**: эмфазис, открытый в первом seg'е и не
|
|
||||||
закрытый, должен продолжаться во втором. Требует хранить emph state per
|
|
||||||
seg (3 бита).
|
|
||||||
- Compact way: добавить параллельный массив `seg_meta[MAX_SEGS]` по 1 байту
|
|
||||||
— пакует marker_width (4 бита) + emph_state (3 бита) + base_attr_idx
|
|
||||||
(4 бита из таблицы → нужен 2-байтовый seg_meta).
|
|
||||||
- **Hpan для длинных строк**: если wrap выключен, добавить ←/→ для
|
|
||||||
горизонтального скролла >80 cols. Общий механизм с tables (Phase 6).
|
|
||||||
**РЕАЛИЗОВАНО** (light) — `viewport_x` + полный re-render на каждое ←/→.
|
|
||||||
- **Ускорение hpan через ESTEX WINCOPY/WINREST** (deferred): сейчас pan
|
|
||||||
делает полный `render_viewport()` = 30 строк × 80 wrchar. Можно
|
|
||||||
скопировать существующее содержимое viewport'а на N cols влево/вправо
|
|
||||||
через win-copy, потом рендерить только узкую полосу справа/слева
|
|
||||||
(HPAN_STEP cols × 30 rows ≈ 240 wrchar вместо 2400). ESTEX SCROLL
|
|
||||||
горизонталь не поддерживает — нужна именно WINCOPY-операция или
|
|
||||||
rdchar/wrchar loop. Активировать когда ощутится тормоз; сейчас на
|
|
||||||
типовом markdown'е не заметно.
|
|
||||||
|
|
||||||
**v2 — отдельная фича, мимо wrap:**
|
|
||||||
- Toggle подсветки целиком (F3?)
|
|
||||||
- Search по тексту (Ctrl+F / F4)
|
|
||||||
- Links `[text](url)` → синий подчёркнутый text, url прячется
|
|
||||||
- Images `` → `[IMG: alt]`
|
|
||||||
|
|
||||||
### Phase 7 — Links и поиск (post-v1)
|
|
||||||
|
|
||||||
- `[text](url)` → отрисовать только `text` с ярко-синим FG (визуально подчёркнутое)
|
|
||||||
- `` → `[IMG: alt]` в скобках
|
|
||||||
- Search по тексту (F3 / Ctrl+F): инкрементальный, подсветка совпадений
|
|
||||||
|
|
||||||
### Phase 8 — F8 Raw / Render toggle
|
|
||||||
|
|
||||||
Переключатель режима отображения: при включённом Raw показывается исходный
|
|
||||||
текст файла как есть — все markdown-маркеры (`#`, `**`, `_`, `` ` ``, `|`,
|
|
||||||
`>`, `-`, etc.) рендерятся литералами с `ATTR_TEXT`, без классификации.
|
|
||||||
Полезно когда:
|
|
||||||
- нужно увидеть точную разметку (отлаживание .md, скриншоты, копирование)
|
|
||||||
- markdown-классификатор ошибся и хочется увидеть оригинал
|
|
||||||
- хочется быстро сравнить "до/после" рендера
|
|
||||||
|
|
||||||
**Поведение:**
|
|
||||||
- F8 переключает `render_mode` (1=render, 0=raw); меню показывает обратное
|
|
||||||
действие ("Raw" когда сейчас render, "Render" когда сейчас raw) — той же
|
|
||||||
логикой что F2/Wrap/Unwrap.
|
|
||||||
- В Raw режиме: `render_line()` идёт по короткому пути — никакого
|
|
||||||
`classify_line`, `is_fence_delim`, `is_code_body`, inline-эмфазиса; просто
|
|
||||||
байтовый дамп FILE_BUF от seg-offset до next-seg/EOL с tab-expansion и
|
|
||||||
ATTR_TEXT.
|
|
||||||
- Раздельно от F2: оба режима независимы (можно Raw+Wrap, Raw+Truncate,
|
|
||||||
Render+Wrap, Render+Truncate). Wrap-логика в `index_lines` работает в
|
|
||||||
обоих случаях одинаково (опирается на визуальные колонки независимо от
|
|
||||||
раскраски).
|
|
||||||
- Статус-бар: добавить индикатор `[R]` / `[V]` (Raw / View) или текстом
|
|
||||||
`RAW` рядом с именем файла.
|
|
||||||
|
|
||||||
**Минимальная реализация:**
|
|
||||||
- Один новый static `uint8_t render_mode = 1;`
|
|
||||||
- В `render_line()`: на самом верху `if (!render_mode) { … raw render … return; }`
|
|
||||||
- В `render_menu()`: добавить ярлык F8 рядом с F2.
|
|
||||||
- В главном цикле: `case KEY_F8: toggle_render(); break;`
|
|
||||||
- `toggle_render()` отличается от `toggle_wrap()` тем, что НЕ перестраивает
|
|
||||||
`line_offset[]` (wrap-сегментация не меняется), только перерендерит экран.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## API-новинки в libc (минимальные)
|
|
||||||
|
|
||||||
### `getkey()` — extended key reader (в libc)
|
|
||||||
|
|
||||||
Текущий `getch()` теряет scan code расширенных клавиш (возвращает только E=ASCII). Добавляем **сразу в `libc/conio/conio.c`** новую функцию рядом с `getch()`:
|
|
||||||
|
|
||||||
```c
|
|
||||||
// Returns scan in high byte, ASCII in low byte.
|
|
||||||
// Extended keys (arrows, F-keys, PgUp/PgDn, Home/End): ASCII=0, scan code в high byte.
|
|
||||||
// Plain keys: ASCII в low byte; high byte содержит positional scan (бит 7 = Ctrl/Alt/Shift modifier).
|
|
||||||
uint16_t getkey(void) __naked {
|
|
||||||
__asm
|
|
||||||
ld c, #0x30 ; ESTEX WAITKEY
|
|
||||||
rst #0x10 ; A=ASCII, D=scan, E=ASCII
|
|
||||||
ld e, a ; ensure E=ASCII even if E clobbered
|
|
||||||
ret ; SDCC __sdcccall(1): возврат uint16_t в DE (D=scan, E=ASCII)
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Дополнительно — **в `libc/include/conio.h`** прописать прототип и константы scan-кодов:
|
|
||||||
|
|
||||||
```c
|
|
||||||
uint16_t getkey(void);
|
|
||||||
|
|
||||||
/* Scan codes for getkey() high byte when ASCII=0 (extended keys). */
|
|
||||||
#define KEY_F1 0x0E
|
|
||||||
#define KEY_F2 0x0F
|
|
||||||
#define KEY_F3 0x10
|
|
||||||
#define KEY_F4 0x11
|
|
||||||
#define KEY_F5 0x12
|
|
||||||
#define KEY_F6 0x13
|
|
||||||
#define KEY_F7 0x14
|
|
||||||
#define KEY_F8 0x15
|
|
||||||
#define KEY_F9 0x16
|
|
||||||
#define KEY_F10 0x17
|
|
||||||
#define KEY_F11 0x18
|
|
||||||
#define KEY_F12 0x19
|
|
||||||
#define KEY_END 0x24
|
|
||||||
#define KEY_DOWN 0x25
|
|
||||||
#define KEY_PGDN 0x26
|
|
||||||
#define KEY_LEFT 0x27
|
|
||||||
#define KEY_RIGHT 0x29
|
|
||||||
#define KEY_HOME 0x2A
|
|
||||||
#define KEY_UP 0x2B
|
|
||||||
#define KEY_PGUP 0x2C
|
|
||||||
#define KEY_INS 0x23
|
|
||||||
#define KEY_DEL 0x22
|
|
||||||
```
|
|
||||||
|
|
||||||
**Скан-коды (из docs/converted/ProgrammerManual.txt:2143-2323):**
|
|
||||||
| Клавиша | scan | Клавиша | scan |
|
|
||||||
|---|---|---|---|
|
|
||||||
| F1 | 0x0E | Up | 0x2B |
|
|
||||||
| F10 | 0x17 | Down | 0x25 |
|
|
||||||
| F11 | 0x18 | Left | 0x27 |
|
|
||||||
| F12 | 0x19 | Right | 0x29 |
|
|
||||||
| | | PgUp | 0x2C |
|
|
||||||
| | | PgDn | 0x26 |
|
|
||||||
| | | Home | 0x2A |
|
|
||||||
| | | End | 0x24 |
|
|
||||||
|
|
||||||
### Что **переиспользуем** из существующей libc
|
|
||||||
|
|
||||||
- `open/read/lseek/close` — `libc/io/{open,read,lseek}.c` (POSIX wrappers)
|
|
||||||
- `mem_alloc_pages/mem_free_block/mem_get_page` — `libc/mem/mem_alloc.c`
|
|
||||||
- `sprinter_page_w3()` — inline `__sfr` write в `libc/include/sprinter.h:113`
|
|
||||||
- `bank_read_w3/bank_write_w3` — `libc/mem/bank_io_w3.c` (для fallback или v2 multi-page)
|
|
||||||
- `wrchar(x, y, ch, attr)` — `libc/conio/conio.c:476` (без auto-scroll, идеально для viewport)
|
|
||||||
- `clrscr_attr(attr)` — `libc/conio/conio.c:395`
|
|
||||||
- `gotoxy/wherex/wherey` — `libc/conio/conio.c:412-462` (если нужно)
|
|
||||||
- `kbhit()` — `libc/conio/conio.c:22` (для non-blocking опроса, опционально)
|
|
||||||
- `dec16/dec8` — `libc/stdio/dec_print.c` (для status bar: текущая строка / total / %)
|
|
||||||
- `COLOR(fg, bg)` макрос — `libc/include/conio.h:152`
|
|
||||||
- Цветовые константы `COLOR_*` — `libc/include/conio.h:145`
|
|
||||||
- `strlen/memcpy/memset` — z80.lib (НЕ переписывать)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Структура исходников
|
|
||||||
|
|
||||||
```
|
|
||||||
examples/mdview/
|
|
||||||
├── Makefile # стандартный pattern (см. examples/cat/Makefile)
|
|
||||||
├── mdview.c # Phase 1: всё в одном файле (main, keys, indexing, render)
|
|
||||||
├── SAMPLE.MD # тестовый markdown
|
|
||||||
└── README.md # описание и controls
|
|
||||||
```
|
|
||||||
|
|
||||||
После Phase 3 раскидать по модулям (если суммарный размер > ~6KB):
|
|
||||||
```
|
|
||||||
mdview.c — main loop, status/menu bars, key dispatch
|
|
||||||
mdrender.c — line rendering with MD inline parser
|
|
||||||
mdindex.c — file load + line indexing
|
|
||||||
```
|
|
||||||
|
|
||||||
### Сборка
|
|
||||||
|
|
||||||
```makefile
|
|
||||||
PROJ ?= ../..
|
|
||||||
SPRINTER_CC := $(PROJ)/bin/sprinter-cc
|
|
||||||
mdview.exe: mdview.c
|
|
||||||
$(SPRINTER_CC) --memory small -o $@ mdview.c
|
|
||||||
```
|
|
||||||
|
|
||||||
`--memory small`: код+данные в W2 (DSS даёт нужное число страниц); файл — отдельная EMM-страница в W3.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Структура mdview.c (Phase 1, эскиз)
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sprinter.h>
|
|
||||||
#include <sprinter_mem.h>
|
|
||||||
|
|
||||||
#define VIEW_TOP 1
|
|
||||||
#define VIEW_BOT 30 // inclusive
|
|
||||||
#define VIEW_H 30
|
|
||||||
#define SCREEN_W 80
|
|
||||||
#define MAX_LINES 2048
|
|
||||||
#define FILE_BUF ((char*)0xC000) /* W3 — EMM page mapped here */
|
|
||||||
#define TAB_STOP 4
|
|
||||||
|
|
||||||
#define ATTR_TEXT COLOR(COLOR_WHITE, COLOR_BLACK)
|
|
||||||
#define ATTR_BAR COLOR(COLOR_WHITE, COLOR_BLUE)
|
|
||||||
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLUE)
|
|
||||||
#define ATTR_MENU_T COLOR(COLOR_WHITE, COLOR_BLUE)
|
|
||||||
|
|
||||||
static uint16_t line_off[MAX_LINES];
|
|
||||||
static uint16_t n_lines;
|
|
||||||
static uint16_t top_line;
|
|
||||||
static uint16_t file_size;
|
|
||||||
static uint8_t file_blk;
|
|
||||||
static char filename[64];
|
|
||||||
|
|
||||||
static int load_file(const char *path); // open, alloc EMM page, map W3, read, close
|
|
||||||
static void index_lines(void); // scan FILE_BUF, fill line_off[]
|
|
||||||
static void render_status(void); // row 0
|
|
||||||
static void render_menu(void); // row 31
|
|
||||||
static void render_line(uint16_t idx, uint8_t row); // one line @ row
|
|
||||||
static void render_viewport(void); // VIEW_H lines starting from top_line
|
|
||||||
static void scroll_up(uint16_t n);
|
|
||||||
static void scroll_down(uint16_t n);
|
|
||||||
static void help_screen(void); // F1
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if (argc < 2) { puts("Usage: mdview <file.md>"); return 1; }
|
|
||||||
if (load_file(argv[1]) < 0) { puts("load error"); return 1; }
|
|
||||||
index_lines();
|
|
||||||
clrscr_attr(ATTR_TEXT);
|
|
||||||
render_menu();
|
|
||||||
render_status();
|
|
||||||
render_viewport();
|
|
||||||
for (;;) {
|
|
||||||
uint16_t k = getkey();
|
|
||||||
uint8_t ascii = k & 0xFF;
|
|
||||||
uint8_t scan = (k >> 8) & 0x7F; // strip mod bit
|
|
||||||
if (ascii) {
|
|
||||||
if (ascii == 0x1B) break; // Esc → exit
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (scan) {
|
|
||||||
case KEY_F10: goto exit;
|
|
||||||
case KEY_F1: help_screen(); break;
|
|
||||||
case KEY_UP: scroll_up(1); break;
|
|
||||||
case KEY_DOWN: scroll_down(1); break;
|
|
||||||
case KEY_PGUP: scroll_up(VIEW_H); break;
|
|
||||||
case KEY_PGDN: scroll_down(VIEW_H); break;
|
|
||||||
case KEY_HOME: top_line = 0;
|
|
||||||
render_viewport(); break;
|
|
||||||
case KEY_END: /* clamp to last viewport */ break;
|
|
||||||
}
|
|
||||||
render_status();
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
mem_free_block(file_blk);
|
|
||||||
clrscr_attr(ATTR_TEXT);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
### Phase 1
|
|
||||||
1. Подготовить `SAMPLE.MD` ~5 KB (заголовки, абзацы, списки) — рендериться будет plain.
|
|
||||||
2. `cd examples/mdview && make`
|
|
||||||
3. `python make_disk.py mdview.exe SAMPLE.MD → mc.img && ./run_mame.sh`
|
|
||||||
4. Проверить:
|
|
||||||
- Status bar показывает `SAMPLE.MD L 1-30 / N X%`
|
|
||||||
- Menu bar внизу
|
|
||||||
- ↑/↓: 1 строка
|
|
||||||
- PgUp/PgDn: 30 строк, корректное clamp на границах
|
|
||||||
- Home: top_line=0
|
|
||||||
- End: top_line = total_lines - VIEW_H
|
|
||||||
- F1: показывает help, любая клавиша возвращает
|
|
||||||
- F10 / Esc: выход, экран очищен
|
|
||||||
5. Edge cases: пустой файл, файл из одной строки, файл с очень длинной строкой (>80), CRLF и LF mixed.
|
|
||||||
|
|
||||||
### Phase 2-4
|
|
||||||
Расширять `SAMPLE.MD` с фичами по мере добавления, визуально верифицировать в MAME. Скриншот-сравнение опционально.
|
|
||||||
|
|
||||||
### Регрессии
|
|
||||||
- Никаких изменений в libc на Phase 1 кроме (потенциально) добавления `getkey()` в `libc/conio/conio.c` — если так, прогнать `examples/conio2` и `examples/filetest` чтобы убедиться что ничего не сломалось.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Решения по неоднозначностям
|
|
||||||
|
|
||||||
1. **Word-wrap vs truncate**: v1 = truncate (просто). v2 — F2 toggle wrap.
|
|
||||||
2. **Горизонтальный скроллинг**: v1 — нет; v2 — `←/→` сдвиг viewport по столбцам.
|
|
||||||
3. **Tab handling**: преобразование при рендере, **tabstop = 4** (стандарт MD). Оригинал в W3 не трогаем.
|
|
||||||
4. **UTF-8**: рендерим байты как есть. Если файл в CP866 — отрисуется кириллицей через системный фонт. UTF-8 — пока не поддерживаем (визуально будет каша на не-ASCII символах; детект и предупреждение — в v2).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 6 — Полный layout таблиц (deferred)
|
|
||||||
|
|
||||||
Сейчас (Phase 4-tables) таблицы рисуются "декоративно" — пайпы и тире
|
|
||||||
заменяются на box-drawing chars, но ширины колонок берутся как есть из
|
|
||||||
исходника. Цель Phase 6 — пересчитать таблицу в нормализованный вид:
|
|
||||||
|
|
||||||
- **Pre-scan таблицы**: пройти все строки одного table-блока, найти
|
|
||||||
максимальную ширину каждой колонки (с учётом съеденных inline-маркеров —
|
|
||||||
визуальный размер, не байтовый).
|
|
||||||
- **Re-emit в буфере**: при загрузке файла (или при первой встрече таблицы)
|
|
||||||
переписать строки в FILE_BUF так, чтобы все ячейки одной колонки имели
|
|
||||||
одинаковую ширину; добавить top/bottom рамки (`┌─┬─┐` / `└─┴─┘`) как
|
|
||||||
синтетические строки. Это позволит сохранить 1:1 соответствие "логическая
|
|
||||||
строка → одна viewport row" без специальной логики при рендере.
|
|
||||||
- **Память**: re-emit может УВЕЛИЧИТЬ файл за счёт padding и доп.рамок.
|
|
||||||
Если буфер близок к 16KB — отрезать таблицу и пометить её overflow'ом.
|
|
||||||
- **Горизонтальный скроллинг**: если итоговая ширина таблицы (или любой
|
|
||||||
строки) > SCREEN_W = 80 — добавить ←/→ для horizontal pan. Это будет
|
|
||||||
общий механизм для длинных строк (см. также wrap mode), не только таблиц.
|
|
||||||
- **Выравнивание из separator-row**: `:-` → left, `-:` → right, `:-:` →
|
|
||||||
center; учитывать при padding'е содержимого ячейки.
|
|
||||||
- **Шаги реализации**:
|
|
||||||
1. Walking pass по фенсам/таблицам прямо в `index_lines()` — собрать
|
|
||||||
extents всех таблиц.
|
|
||||||
2. Для каждой таблицы — определить ширины колонок.
|
|
||||||
3. Решение: rewrite-in-buffer (проще для рендера, но мутирует исходник)
|
|
||||||
vs render-time layout (cleaner, но требует отдельной структуры
|
|
||||||
описания layout'а на каждую таблицу).
|
|
||||||
4. Hpan: общий `viewport_x_offset` для всего экрана, или отдельный
|
|
||||||
"широкий режим" только внутри таблиц.
|
|
||||||
|
|
||||||
> Не блокирующая фича. Запускать когда станет понятен типовой источник
|
|
||||||
> markdown-файлов (узкие читалки → достаточно текущего декоратора;
|
|
||||||
> широкие README с большими таблицами → нужен полный layout).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase ∞ — Кэш рендеренных строк (low priority)
|
|
||||||
|
|
||||||
Отложено: текущая скорость более чем достаточна. Активировать если появится
|
|
||||||
сценарий, где видна задержка PgUp/PgDn (например, при тяжёлом inline-парсере
|
|
||||||
v2 с UTF-8 / linkifier / таблицами).
|
|
||||||
|
|
||||||
**Кэш отформатированных строк** (W3, отдельная EMM-страница):
|
|
||||||
|
|
||||||
```
|
|
||||||
Cache layout (16 KB EMM page, всего 16000 байт используется):
|
|
||||||
slot 0: 80 chars + 80 attrs = 160 bytes @ offset 0
|
|
||||||
...
|
|
||||||
slot 99: 80 chars + 80 attrs = 160 bytes @ offset 15840
|
|
||||||
|
|
||||||
Cache tags (W2 static): uint16_t cache_tag[100] = 200 bytes
|
|
||||||
cache_tag[i] = line_id, или 0xFFFF = invalid
|
|
||||||
```
|
|
||||||
|
|
||||||
Стратегия — **direct map (no LRU)**: `slot = line_id % 100`. Коллизия → вытеснение.
|
|
||||||
|
|
||||||
**Batched viewport render**: 2 page-swap'а на ВЕСЬ viewport (cache → file → cache),
|
|
||||||
не 60 как при наивной реализации.
|
|
||||||
|
|
||||||
При сборке: `cache_blk = mem_alloc_pages(1)` после `file_blk`; `mem_free_block` на exit.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Что отложено в v2
|
|
||||||
|
|
||||||
- ~~Файлы >16 KB~~ — **сделано в v1.5** (до 128 KB через 1–8 EMM-страниц + lazy map в W3).
|
|
||||||
- ~~Word wrap~~ — **сделано** (Phase 5, F2 toggle).
|
|
||||||
- Search (Find / Find next) — F3 / F4.
|
|
||||||
- F8 Raw / Render toggle — спецификация в Phase 8.
|
|
||||||
- Links `[text](url)` + image alt — Phase 7.
|
|
||||||
- Tables — Phase 6 (полный layout).
|
|
||||||
- Toggle highlight on/off — частный случай F8 Raw.
|
|
||||||
@@ -1,570 +0,0 @@
|
|||||||
# �« : ⥪áâ®¢ë© Markdown viewer ¤«ï Sprinter (`examples/mdview`)
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
’¥á⮢ ï ªàã¯ ï § ¤ ç - ¯à®¢¥à¨âì èã libc ¥âਢ¨ «ì®¬ interactive-¯à¨«®¦¥¨¨ (¯®«®íªà ë© UI, ä ©«®¢ë© I/O, ¯ àá¥à). � à ««¥«ì® ¤ áâ å®à®è¨© showcase ¯« âä®à¬ë ¨ ¯®¬®¦¥â ¢ëâ é¨âì ¥¤®¤¥«ª¨ ¢ conio/io. Š®¥ç ï æ¥«ì: viewer ¤«ï `.md` ä ©«®¢ á ¯®¤á¢¥âª®© á¨â ªá¨á , ¢¨£ 樥© ¯® ⥪áâã ¨ ¯®áâà ¨çë¬ áªà®««¨£®¬.
|
|
||||||
|
|
||||||
**Ž£à ¨ç¥¨ï v1 (§ 䨪á¨à®¢ ë ¯®«ì§®¢ ⥫¥¬):**
|
|
||||||
- POSIX file API (open/read/lseek/close); FILE pointer / fread / fgets - ¥ ¨á¯®«ì§®¢ âì
|
|
||||||
- �®¤á¢¥âª ç¥à¥§ 梥â: **à §¬¥à § £®«®¢ª ** -> 梥â èà¨äâ ; **bold/italic** -> 梥â ä® (¬®®è¨à¨ë© ä®â ¡¥§ ¦¨à®£®/ªãàᨢ®£® ç¥àâ ¨ï)
|
|
||||||
|
|
||||||
**� áè¨à¥¨ï, ॠ«¨§®¢ ë¥ ¯®á«¥ ¯« :**
|
|
||||||
- ” ©«ë ¤® 128 KB (1-8 EMM-áâà ¨æ, lazy map ¢ W3 ç¥à¥§ `fb()/map_page()`) - ¨§ ç «ì® ¡ë«® ¢ v2
|
|
||||||
- €¨¬¨à®¢ ë© spinner ¨ ¯à¥¤¢ à¨â¥«ì ï ®âà¨á®¢ª UI ¯à¨ áâ à⥠- UX
|
|
||||||
- Inline emphasis: `_`/`*`/`**` ¯®¤ç¨ïîâáï XOR flanking-¯à ¢¨«ã (whitespace ஢® á ®¤®© áâ®à®ë), `COLOR_YELLOW` ¨ `2 * 3` ®áâ îâáï «¨â¥à « ¬¨
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ’¥ªã騩 áâ âãá (2026-06-05)
|
|
||||||
|
|
||||||
| Phase | ‘â âãá | Š®¬¬¥â ਩ |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 Plain text + nav | | § £à㧪 , ¨¤¥ªá æ¨ï, status/menu, ^V/PgUp/PgDn/Home/End/F1/F10/Esc |
|
|
||||||
| 2 Headers + HR | | H1..H4, `---`/`***`/`___` (á >=3 marker'®¢) |
|
|
||||||
| 3 Inline emphasis | | `**` / `*` / `_` / `` ` ``; XOR flanking (á¬. ¢ëè¥) |
|
|
||||||
| 4 Lists / quote / fenced code | | `- / * / +`, `N. / N)`, `> `, ``` ``` ```; light nested lists |
|
|
||||||
| 4-tables | | â ¡«¨æë ®â«®¦¥ë ¢¬¥á⥠á Phase 6 |
|
|
||||||
| 5 Wrap / Unwrap (F2) | | wrap-by-default; soft wrap; F2 ¯¥à¥ª«îç ¥â; hpan <-/-> ¢ truncate-०¨¬¥ |
|
|
||||||
| 6 �®«ë© layout â ¡«¨æ | | deferred |
|
|
||||||
| 7 Links + search | | deferred |
|
|
||||||
| 8 F8 Raw toggle | | deferred |
|
|
||||||
| Cache ।¥à¥ëå áâப | | ¥ 㦮 ¯® ᪮à®á⨠|
|
|
||||||
|
|
||||||
**UX-¯®¯à ¢ª¨ (®â¤¥«ì® ®â phase-¯« , 2026-06-05):**
|
|
||||||
- UI (menu + title bar) ®âà¨á®¢ë¢ ¥âáï „Ž `load_file`/`index_lines` - ¯®«ì§®¢ ⥫ì áà §ã ¢¨¤¨â ¨â¥à䥩á, ¥ çñàë© íªà
|
|
||||||
- Title bar: `MDVIEW <spinner> <filename>` (3 ¯à®¡¥« ¬¥¦¤ã MDVIEW ¨ filename, slot ᯨ¥à - col 8)
|
|
||||||
- Spinner ªàãâ¨âáï ¢® ¢à¥¬ï `load_file` (¯® áâà ¨æ¥) ¨ `index_lines` (à § ¢ 32 «®£¨ç¥áª¨å áâப¨), ¢ª«îç ï `toggle_wrap`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## €àå¨â¥ªâãà
|
|
||||||
|
|
||||||
### � ᪫ ¤ª íªà (80x32, ⥪áâ mode 0x03)
|
|
||||||
|
|
||||||
```
|
|
||||||
Row 0: MDVIEW ³ mdview.md ³ L 1-30 / 142 ³ 21% <- status bar (BG=blue, FG=white)
|
|
||||||
|
|
||||||
³ ÀÄÄ filename @ col 10
|
|
||||||
ÀÄÄÄÄÄÄÄÄÄÄ spinner slot @ col 8 (anim. while busy)
|
|
||||||
Row 1:
|
|
||||||
... document viewport (30 rows)
|
|
||||||
Row 30:
|
|
||||||
Row 31: F1 Help F2 Wrap F10 Exit <- menu bar (BG=blue, FG=cyan)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Viewport = 30 áâப x 80 á⮫¡æ®¢.
|
|
||||||
- Status / menu ।¥àïâáï ç¥à¥§ `wrchar()` (¡¥§ ¢â®-áªà®«« ), viewport - ç¥à¥§ `LOCATE` + ¯®á¨¬¢®«ìë© `wrchar()` (⮦¥ ¡¥§ ¢â®-áªà®«« , áâ ¢¨â ¨ char ¨ attr § ®¤¨ call).
|
|
||||||
|
|
||||||
### � ¬ïâì
|
|
||||||
|
|
||||||
Memory mode: **`small`** - DSS ®â¢®¤¨â ¯®¤ è ®¡à § **¤¢ ¡ ª (W1 + W2)** = 32 KB á㬬 à® (CODE ¢ W1, DATA + STACK + HEAP ¢ W2). �⮣® ¤®«¦® å¢ â¨âì ç⮡ë �… § ¢®¤¨âì `__banked` äãªæ¨¨. W3 ®áâ ñâáï ¯®«®áâìî ᢮¡®¤ë¬ ¤«ï ¬ ¯¯¨£ ¡®«ìè¨å ¡ãä¥à®¢:
|
|
||||||
|
|
||||||
```
|
|
||||||
W0 (0x0000-0x3FFF): ESTEX (system, untouchable)
|
|
||||||
W1 (0x4000-0x7FFF): CODE (small mode page 1)
|
|
||||||
W2 (0x8000-0xBFFF): DATA + STACK + HEAP (small mode page 2)
|
|
||||||
W3 (0xC000-0xFFFF): paged window - «¥¨¢® ¯¥à¥ª«îç ¥âáï ¬¥¦¤ã EMM-áâà ¨æ ¬¨
|
|
||||||
ä ©« (¤® 8 áâà ¨æ = 128 KB). `cur_page` ªíè¨àã¥â
|
|
||||||
⥪ãéãî mapping, `fb(p)` ¬ ¯¯¨â ã¦ãî áâà ¨æã ¯à¨
|
|
||||||
¯¥à¢®¬ ®¡à 饨¨.
|
|
||||||
```
|
|
||||||
|
|
||||||
�®ç¥¬ã small + W3:
|
|
||||||
- 32 KB ª®¤+¤ ë¥ á ¡®«ì訬 § ¯ ᮬ -> ¥â ¡ ª¨£
|
|
||||||
- W3 - áâ ¤ àâë© paged window, ¯®¤ ¥£® ã á 㦥 ¥áâì `bank_io_w3` API
|
|
||||||
- ” ©«ë ¤® 128 KB ¯®¤¤¥à¦¨¢ îâáï ⨢®: `mem_alloc_pages(pages_needed)` ¯®¤ ¢¥áì ä ©«; `map_page()` ç¥à¥§ `sprinter_page_w3()`; `fb(p)` - ¥¤¨ ï â®çª ¤®áâ㯠¨§ ¨¤¥ªá â®à ¨ ।¥à .
|
|
||||||
|
|
||||||
‘â ⨪¨ (¢ W2):
|
|
||||||
- `line_offset[MAX_LINES]` - uint16_t ᬥ饨¥ ª ¦¤®© áâப¨ ¢ ä ©«®¢®© áâà ¨æ¥ (4 KB 2048 áâப)
|
|
||||||
- `cache_tag[CACHE_N]` - uint16_t ⥣ á«®â (200 ¡ ©â 100 ᫮⮢, ¯®ï¢«ï¥âáï ¢ Phase 3)
|
|
||||||
- `filename[64]`, `top_line`, `total_lines`, `file_size`, `file_blk`, `cache_blk` - ¥¤¨¨æë ¡ ©â
|
|
||||||
|
|
||||||
**FILE_BUF**: `((char*)0xC000)` - 䨪á¨à®¢ ï ¤à¥á æ¨ï ¢ W3 ¯®á«¥ ¬ ¯¯¨£ 㦮© áâà ¨æë.
|
|
||||||
|
|
||||||
### �®â®ª ¤ ëå
|
|
||||||
|
|
||||||
```
|
|
||||||
main -> open() -> read() chunks 1KB -> write to W1 (mapped EMM page) -> close()
|
|
||||||
-> index_lines() (®¤® ᪠¨à®¢ ¨¥, § ¯®«ï¥â line_offset[])
|
|
||||||
-> render_viewport() + main loop { getkey(); handle(); render_status(); render_viewport() }
|
|
||||||
```
|
|
||||||
|
|
||||||
**’®ª®áâì á `read()`**: ESTEX READ § ¯¨áë¢ ¥â ¯® dst-㪠§ â¥«î ¢ ¤à¥á®¬ ¯à®áâà á⢥ ¢ë§ë¢ î饣®. �®áª®«ìªã ¬ë ¬ ¯¯¨¬ EMM-áâà ¨æã ¢ W3 (0xC000) „Ž ¢ë§®¢ read(), 㪠§ ⥫ì 0xC000+offset ¢ «¨¤¥. …᫨ ¢ëïá¨âáï çâ® BIOS âண ¥â W3 ¢® ¢à¥¬ï read (£à ä¨ç¥áª¨© ¢¨¤¥®¡ãä¥à ¯® 㬮«ç ¨î ¢ W3 ¯à¨ £à ä¨ç¥áª¨å ०¨¬ å, ® ¢ ⥪á⮢®¬ - ¤®«¦¥ ¡ëâì ᢮¡®¤¥) - fallback: ç¨â âì ¢ 1 KB ¡ãä¥à ¢ W2 ¨ ª®¯¨à®¢ âì ¢ W3 ç¥à¥§ `bank_write_w3()`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## �¥ «¨§ æ¨ï - ¯®íâ ¯ ï
|
|
||||||
|
|
||||||
### Phase 1 - Plain text viewer (MVP)
|
|
||||||
|
|
||||||
**—â® à ¡®â ¥â:**
|
|
||||||
- ‡ £à㧪 ä ©« (`open/read/close`) ¢ W1-áâà ¨æã
|
|
||||||
- ˆ¤¥ªá æ¨ï áâப (LF / CRLF à §¤¥«¨â¥«¨)
|
|
||||||
- Status bar: ¨¬ï ä ©« , L N-M / Total, ¯à®æ¥â áªà®««¨£
|
|
||||||
- Menu bar: `F1 Help F10 Exit`
|
|
||||||
- � ¢¨£ æ¨ï: ^/V (1 áâப ), PgUp/PgDn (30 áâப), Home/End ( ç «®/ª®¥æ), Esc/F10 (¢ë室), F1 (help screen)
|
|
||||||
- Ž¡à¥§ª áâப ¤«¨¥¥ 80 ᨬ¢®«®¢ (¡¥§ word-wrap)
|
|
||||||
- –¢¥â : ⥪áâ ¡¥«ë© çñ஬; status/menu - ¡¥«ë© ᨥ¬
|
|
||||||
|
|
||||||
**Šà¨â¨çë¥ ä ©«ë:**
|
|
||||||
- `examples/mdview/mdview.c` - main, key loop, rendering, indexing (one-file MVP)
|
|
||||||
- `examples/mdview/Makefile`
|
|
||||||
- `examples/mdview/SAMPLE.MD` - â¥áâ®¢ë© markdown ä ©«
|
|
||||||
|
|
||||||
### Phase 2 - Headers ¨ £®à¨§®â «ì ï «¨¨ï
|
|
||||||
|
|
||||||
**MD ä¨ç¨:**
|
|
||||||
- `# H1` -> ïમ-¦ñ«âë© (COLOR_YELLOW = 14) çñ஬
|
|
||||||
- `## H2` -> ïમ-£®«ã¡®© (COLOR_LBLUE = 11)
|
|
||||||
- `### H3` -> ïમ-§¥«ñë© (COLOR_LGREEN = 10)
|
|
||||||
- `#### H4+` -> á¥àë© (COLOR_GREY = 8)
|
|
||||||
- `---` / `***` ®â¤¥«ì®© áâப¥ -> «¨¨ï 0xC4 (£®à¨§®â «ì ï à ¬ª ASCII) ¢® ¢áî è¨à¨ã
|
|
||||||
|
|
||||||
### Phase 3 - Inline emphasis
|
|
||||||
|
|
||||||
**� àá¥à inline (per-line, runs ¢ ®¤ã áâபã):**
|
|
||||||
- `**bold**` -> ATTR_TEXT_BOLD
|
|
||||||
- `*italic*` -> ATTR_TEXT_ITALIC
|
|
||||||
- `_underscore_` -> ATTR_TEXT_UNDERSORE
|
|
||||||
- `` `code` `` -> ATTR_TEXT_CODE
|
|
||||||
- Œ થàë `**`/`*`/`_`/`` ` `` �… ।¥àïâáï (áꥤ îâáï)
|
|
||||||
|
|
||||||
State machine: ®¤¨ ªâ¨¢ë© áâ¨«ì ®¤®¢à¥¬¥® (¡¥§ ¢«®¦¥®áâ¨); ª®ä«¨ªâãî騩 ¬ àª¥à ¯à¨ ç㦮¬ ªâ¨¢®¬ á⨫¥ ¢áñ à ¢® ª®áìâáï (zero-width) ¤«ï á¨åந§ 樨 è¨à¨ë á index_lines. ‘®áâ®ï¨¥ á¡à áë¢ ¥âáï ª ¦¤®© áâப¥.
|
|
||||||
|
|
||||||
**Flanking-¯à ¢¨«® (CommonMark intraword, ॠ«¨§®¢ ® ¯®á«¥ ¨§ ç «ì®£® ¯« ):**
|
|
||||||
- `*` / `**` / `_` áç¨â îâáï markdown-¬ થ஬ ⮫쪮 ¥á«¨ whitespace/EOL
|
|
||||||
஢® á Ž„�މ áâ®à®ë (XOR).
|
|
||||||
- ‘«ãç ¨ "®¡ whitespace" (`2 * 3`, `2 ** 3`) -> «¨â¥à «ë ( à¨ä¬¥â¨ª ).
|
|
||||||
- ‘«ãç ¨ "¨ ®¤®£® whitespace" (`COLOR_YELLOW`, `FILE*/fread`, `foo*bar*baz`)
|
|
||||||
-> «¨â¥à «ë (intraword).
|
|
||||||
- Backtick (`` ` ``) flanking �… âॡã¥â - `` `code` `` à ¡®â ¥â ¡¥§ ¯à®¡¥«®¢.
|
|
||||||
- �à ¢¨«® ¯à¨¬¥¥® ᨬ¬¥âà¨ç® ¢ 4 ¬¥áâ å (`index_lines`, cont-render,
|
|
||||||
®á®¢®© inline-¯ àá¥à, truncation peek), ¨ ç¥ wrap-¨¤¥ªá â®à ¨
|
|
||||||
।¥à à §ê¥¤ãâáï ¯® è¨à¨¥.
|
|
||||||
|
|
||||||
> **Šíè ®âä®à¬ â¨à®¢ ëå áâப** - ®â«®¦¥ ¢ á ¬ë© ª®¥æ, á¬. "Phase : ®¯â¨¬¨§ 樨".
|
|
||||||
> ‘ª®à®á⨠⥪ã饣® ¨¢®£® ।¥à å¢ â ¥â 80x30 = 2400 wrchar / ª ¤à; PgUp/PgDn ¢¨§ã «ì® ¬£®¢¥¥.
|
|
||||||
|
|
||||||
### Phase 4 - Block elements (¡¥§ â ¡«¨æ)
|
|
||||||
|
|
||||||
- Œ ન஢ ë¥ á¯¨áª¨: `- foo`, `* foo`, `+ foo` -> ¯à¥ä¨ªá `o` (0x07) + ¯à®¡¥«; 梥⠬ થà ïàç¥ ®á®¢®£®
|
|
||||||
- �㬥஢ ë¥ á¯¨áª¨: `1. foo`, `2. foo` -> ª ª ¥áâì (ç¨á«® ®áâ ¢«ï¥¬)
|
|
||||||
- Blockquote: áâப¨ á `> ` -> ¯à¥ä¨ªá `³` (0xB3) á¥à®£® 梥â , ®á⠫쮩 ⥪áâ á«¥£ª ¯à¨£«ãèñë©
|
|
||||||
- Fenced code blocks: `` ``` `` ®âªàë¢ ¥â/§ ªàë¢ ¥â ¡«®ª; ¢á¥ áâப¨ ¬¥¦¤ã - bg=á¥àë©, ¬®®è¨à¨® (¡¥§ inline-¯ àᨣ )
|
|
||||||
- Indented code blocks (4+ ¯à®¡¥«®¢): «®£¨ç® fenced, ® ¡¥§ £® ¬ થà
|
|
||||||
|
|
||||||
**Light nested lists (v1 - ॠ«¨§®¢ ®):**
|
|
||||||
- `classify_line()` ¯à®¯ã᪠¥â leading spaces ¯¥à¥¤ ulist/olist/quote ¬ થ஬,
|
|
||||||
¢®§¢à é ¥â `content_off` ¯®á«¥ ¬ થà -> `content_off - p_start` = indent + marker
|
|
||||||
¡¨â ¢ visible col.
|
|
||||||
- `render_line()` à¨áã¥â leading-spaces ¢ `ATTR_TEXT`, ¯®â®¬ ¬ થà ᤢ¨ã⮩
|
|
||||||
¯®§¨æ¨¨ (col = indent). Marker ¢áñ ¥éñ 䨪á¨à®¢ ¯à¨ £®à¨§®â «ì®¬ pan'¥.
|
|
||||||
- HR / header / fence delim ®áâ îâáï áâண® col-0 (CommonMark à §à¥è ¥â ¤® 3
|
|
||||||
¢¥¤ãé¨å ¯à®¡¥«®¢ ¤«ï ¨å - ã¯à®á⨫¨).
|
|
||||||
- Tab-indent -> ¥ à ᯮ§ ñâáï ª ª nesting (⮫쪮 spaces).
|
|
||||||
|
|
||||||
**Phase 4-full - ¯®« ï ¯®¤¤¥à¦ª ¢«®¦¥®á⨠(deferred):**
|
|
||||||
- **Tab-indent**: áç¨â âì tab = 4 ¯à®¡¥« ¤«ï ®¯à¥¤¥«¥¨ï ã஢ï.
|
|
||||||
- **Quote nesting** (`> > foo`): ª ¦¤ë© `>` ¯®¤àï¤ = +1 ã஢¥ì, ª ¦¤ë© à¨áã¥âáï
|
|
||||||
®â¤¥«ìë¬ `³` ¢ `ATTR_QUOTE_MARKER` (¢¨§ã «ì ï "«¥áâ¨æ " á«¥¢ ).
|
|
||||||
- **Hanging indent ¢ wrap-continuation**: ª®£¤ `- some very long bullet text
|
|
||||||
that wraps...` - continuation seg ¤®«¦¥ ç¨ âìáï ®â content-col (¯®á«¥
|
|
||||||
¬ થà ), ¥ ®â col 0. ‘¥©ç á continuation ¨¤ñâ ®â col 0 (v1 simplification).
|
|
||||||
’ॡã¥â åà ¨âì `marker_width` per «®£¨ç¥áª ï áâப (8 ¡¨â) ¨«¨ re-classify
|
|
||||||
first seg ¯à¨ ।¥à¥ continuation.
|
|
||||||
- **Lazy continuation**: áâப¨ ¡¥§ ¬ થà , ® á ¯à ¢¨«ìë¬ indent ¯®¤
|
|
||||||
¯à¥¤ë¤ã騬 bullet'®¬, ¤®«¦ë áç¨â âìáï ¯à®¤®«¦¥¨¥¬ ⮣® bullet'
|
|
||||||
(¢¨§ã «ì® - ®¡é¨© attr).
|
|
||||||
- **Strict CommonMark indent rules**: ¢«®¦¥ë© ¯ãªâ ¤®«¦¥ ¡ëâì
|
|
||||||
indent >= content_col த¨â¥«ï, ¨ ç¥ áç¨â ¥âáï breakout. �㦥 ¬¨¨-stack
|
|
||||||
ªâ¨¢ëå ᯨ᪮¢ ¯à¨ ¨¤¥ªá 樨.
|
|
||||||
|
|
||||||
### Phase 5 - Wrap / Unwrap ¤«¨ëå áâப
|
|
||||||
|
|
||||||
„¥ä®«â: **wrap on**. F2 ¯¥à¥ª«îç ¥â; ¢ ¬¥î-¡ ॠ¯®¤¯¨áì ®âà ¦ ¥â ¤¥©á⢨¥
|
|
||||||
("Unwrap" ª®£¤ wrap ¢ª«îçñ, "Wrap" ª®£¤ ¢ëª«îç¥). ‚® ¢à¥¬ï २¤¥ªá 樨
|
|
||||||
ªàãâ¨âáï ᯨ¥à title bar.
|
|
||||||
|
|
||||||
**v1 - ॠ«¨§®¢ ®:**
|
|
||||||
- ޤ¨ ¬ áᨢ `line_offset[2048]` åà ¨â ‚ˆ„ˆŒ›… ᥣ¬¥âë ( ¥ «®£¨ç¥áª¨¥
|
|
||||||
áâப¨); ¡¨âë 0..13 - ¡ ©â®¢®¥ ᬥ饨¥, ¡¨â 15 - CONT-ä« £ continuation.
|
|
||||||
- Wrap-०¨¬: soft wrap ¯®á«¥¤¥¬ ¯à®¡¥«¥ <= 80; hard fallback ¥á«¨
|
|
||||||
¯à®¡¥« ¥â.
|
|
||||||
- Œ થàë í¬ä §¨á (`**`/`*`/`_`/`` ` ``) ¨ header-¯à¥ä¨ªáë (`#`/`##`/...) ¥
|
|
||||||
ãç¨âë¢ îâáï ¢ visible-col ¯à¨ ¯®¨áª¥ â®çª¨ ¯¥à¥®á .
|
|
||||||
- "‘¯¥æ¨ «ìë¥" «®£¨ç¥áª¨¥ áâப¨ ¥ wrap' îâáï ¢®®¡é¥ (®¤ seg-§ ¯¨áì
|
|
||||||
«®£¨ç¥áªãî áâபã): fence delim, table row (header/separator/body), HR.
|
|
||||||
- F2 toggle á®åà ï¥â ¢¨§ã «ìãî ¯®§¨æ¨î ç¥à¥§ `top_offset` ¢ FILE_BUF.
|
|
||||||
- Bitmaps (`in_code`/`in_table`/`is_tab_hdr`) ¯¥à¥áâà ¨¢ îâáï ¢¬¥á⥠á
|
|
||||||
ᥣ¬¥â ¬¨, ¨¤¥ªá¨àãîâáï seg-¨¤¥ªá®¬, ¡¨âë áâ ¢ïâáï ⮫쪮 ¯¥à¢®¬
|
|
||||||
seg'¥ «®£¨ç¥áª®© áâப¨.
|
|
||||||
- Continuation-ᥣ¬¥âë ।¥àïâáï ¢ á⨫¥ "v1: ¯«®áª®" - plain text,
|
|
||||||
¨ª ª¨å markdown-ª« áá¨ä¨ª 権; padding ¤® ª®æ áâப¨ `ATTR_TEXT`.
|
|
||||||
|
|
||||||
**v1.5 - ®â«®¦¥® ¤«ï ¯®«®© ª àâ¨ë wrap:**
|
|
||||||
- **Hanging indent**: continuation ®â ulist/olist/quote ¤®«¦¥ ¢ëà ¢¨¢ âìáï
|
|
||||||
¯®¤ content, ¥ ®â col 0. ’ॡã¥â åà ¨âì marker_width per «®£¨ç¥áª ï
|
|
||||||
áâப ˆ‹ˆ re-classify ¯¥à¢®£® seg' ¯à¨ ।¥à¥ continuation.
|
|
||||||
- **� á«¥¤®¢ ¨¥ base_attr**: continuation ®â header' ¤®«¦¥ á®åà ïâì
|
|
||||||
梥â; continuation ®â code body - ä® ATTR_TEXT_CODE. ’ॡã¥â åà ¨âì
|
|
||||||
1 ¡ ©â `base_attr` per seg ˆ‹ˆ lookup ¯¥à¢®£® seg' .
|
|
||||||
- **Inline emphasis ç¥à¥§ £à ¨æã**: í¬ä §¨á, ®âªàëâë© ¢ ¯¥à¢®¬ seg'¥ ¨ ¥
|
|
||||||
§ ªàëâë©, ¤®«¦¥ ¯à®¤®«¦ âìáï ¢® ¢â®à®¬. ’ॡã¥â åà ¨âì emph state per
|
|
||||||
seg (3 ¡¨â ).
|
|
||||||
- Compact way: ¤®¡ ¢¨âì ¯ à ««¥«ìë© ¬ áᨢ `seg_meta[MAX_SEGS]` ¯® 1 ¡ ©âã -
|
|
||||||
¯ ªã¥â marker_width (4 ¡¨â ) + emph_state (3 ¡¨â ) + base_attr_idx
|
|
||||||
(4 ¡¨â ¨§ â ¡«¨æë -> 㦥 2-¡ ©â®¢ë© seg_meta).
|
|
||||||
- **Hpan ¤«ï ¤«¨ëå áâப**: ¥á«¨ wrap ¢ëª«îç¥, ¤®¡ ¢¨âì <-/-> ¤«ï
|
|
||||||
£®à¨§®â «ì®£® áªà®«« >80 cols. ޡ鍩 ¬¥å ¨§¬ á tables (Phase 6).
|
|
||||||
**�…€‹ˆ‡Ž‚€�Ž** (light) - `viewport_x` + ¯®«ë© re-render ª ¦¤®¥ <-/->.
|
|
||||||
- **“᪮२¥ hpan ç¥à¥§ ESTEX WINCOPY/WINREST** (deferred): ᥩç á pan
|
|
||||||
¤¥« ¥â ¯®«ë© `render_viewport()` = 30 áâப x 80 wrchar. Œ®¦®
|
|
||||||
᪮¯¨à®¢ âì áãé¥áâ¢ãî饥 ᮤ¥à¦¨¬®¥ viewport' N cols ¢«¥¢®/¢¯à ¢®
|
|
||||||
ç¥à¥§ win-copy, ¯®â®¬ ।¥à¨âì ⮫쪮 㧪ãî ¯®«®áã á¯à ¢ /á«¥¢
|
|
||||||
(HPAN_STEP cols x 30 rows 240 wrchar ¢¬¥áâ® 2400). ESTEX SCROLL
|
|
||||||
£®à¨§®â «ì ¥ ¯®¤¤¥à¦¨¢ ¥â - 㦠¨¬¥® WINCOPY-®¯¥à æ¨ï ¨«¨
|
|
||||||
rdchar/wrchar loop. €ªâ¨¢¨à®¢ âì ª®£¤ ®éãâ¨âáï â®à¬®§; ᥩç á
|
|
||||||
⨯®¢®¬ markdown'¥ ¥ § ¬¥â®.
|
|
||||||
|
|
||||||
**v2 - ®â¤¥«ì ï ä¨ç , ¬¨¬® wrap:**
|
|
||||||
- Toggle ¯®¤á¢¥âª¨ 楫¨ª®¬ (F3?)
|
|
||||||
- Search ¯® ⥪áâã (Ctrl+F / F4)
|
|
||||||
- Links `[text](url)` -> ᨨ© ¯®¤çñàªãâë© text, url ¯àïç¥âáï
|
|
||||||
- Images `` -> `[IMG: alt]`
|
|
||||||
|
|
||||||
### Phase 7 - Links ¨ ¯®¨áª (post-v1)
|
|
||||||
|
|
||||||
- `[text](url)` -> ®âà¨á®¢ âì ⮫쪮 `text` á ïમ-ᨨ¬ FG (¢¨§ã «ì® ¯®¤çñàªã⮥)
|
|
||||||
- `` -> `[IMG: alt]` ¢ ᪮¡ª å
|
|
||||||
- Search ¯® ⥪áâã (F3 / Ctrl+F): ¨ªà¥¬¥â «ìë©, ¯®¤á¢¥âª ᮢ¯ ¤¥¨©
|
|
||||||
|
|
||||||
### Phase 8 - F8 Raw / Render toggle
|
|
||||||
|
|
||||||
�¥à¥ª«îç ⥫ì ०¨¬ ®â®¡à ¦¥¨ï: ¯à¨ ¢ª«îçñ®¬ Raw ¯®ª §ë¢ ¥âáï ¨á室ë©
|
|
||||||
⥪áâ ä ©« ª ª ¥áâì - ¢á¥ markdown-¬ થàë (`#`, `**`, `_`, `` ` ``, `|`,
|
|
||||||
`>`, `-`, etc.) ।¥àïâáï «¨â¥à « ¬¨ á `ATTR_TEXT`, ¡¥§ ª« áá¨ä¨ª 樨.
|
|
||||||
�®«¥§® ª®£¤ :
|
|
||||||
- 㦮 㢨¤¥âì â®çãî à §¬¥âªã (®â« ¦¨¢ ¨¥ .md, áªà¨è®âë, ª®¯¨à®¢ ¨¥)
|
|
||||||
- markdown-ª« áá¨ä¨ª â®à ®è¨¡áï ¨ å®ç¥âáï 㢨¤¥âì ®à¨£¨ «
|
|
||||||
- å®ç¥âáï ¡ëáâà® áà ¢¨âì "¤®/¯®á«¥" ।¥à
|
|
||||||
|
|
||||||
**�®¢¥¤¥¨¥:**
|
|
||||||
- F8 ¯¥à¥ª«îç ¥â `render_mode` (1=render, 0=raw); ¬¥î ¯®ª §ë¢ ¥â ®¡à ⮥
|
|
||||||
¤¥©á⢨¥ ("Raw" ª®£¤ ᥩç á render, "Render" ª®£¤ ᥩç á raw) - ⮩ ¦¥
|
|
||||||
«®£¨ª®© çâ® F2/Wrap/Unwrap.
|
|
||||||
- ‚ Raw ०¨¬¥: `render_line()` ¨¤ñâ ¯® ª®à®âª®¬ã ¯ã⨠- ¨ª ª®£®
|
|
||||||
`classify_line`, `is_fence_delim`, `is_code_body`, inline-í¬ä §¨á ; ¯à®áâ®
|
|
||||||
¡ ©â®¢ë© ¤ ¬¯ FILE_BUF ®â seg-offset ¤® next-seg/EOL á tab-expansion ¨
|
|
||||||
ATTR_TEXT.
|
|
||||||
- � §¤¥«ì® ®â F2: ®¡ ०¨¬ ¥§ ¢¨á¨¬ë (¬®¦® Raw+Wrap, Raw+Truncate,
|
|
||||||
Render+Wrap, Render+Truncate). Wrap-«®£¨ª ¢ `index_lines` à ¡®â ¥â ¢
|
|
||||||
®¡®¨å á«ãç ïå ®¤¨ ª®¢® (®¯¨à ¥âáï ¢¨§ã «ìë¥ ª®«®ª¨ ¥§ ¢¨á¨¬® ®â
|
|
||||||
à áªà ᪨).
|
|
||||||
- ‘â âãá-¡ à: ¤®¡ ¢¨âì ¨¤¨ª â®à `[R]` / `[V]` (Raw / View) ¨«¨ ⥪á⮬
|
|
||||||
`RAW` à冷¬ á ¨¬¥¥¬ ä ©« .
|
|
||||||
|
|
||||||
**Œ¨¨¬ «ì ï ॠ«¨§ æ¨ï:**
|
|
||||||
- ޤ¨ ®¢ë© static `uint8_t render_mode = 1;`
|
|
||||||
- ‚ `render_line()`: á ¬®¬ ¢¥àåã `if (!render_mode) { ... raw render ... return; }`
|
|
||||||
- ‚ `render_menu()`: ¤®¡ ¢¨âì ïà«ëª F8 à冷¬ á F2.
|
|
||||||
- ‚ £« ¢®¬ 横«¥: `case KEY_F8: toggle_render(); break;`
|
|
||||||
- `toggle_render()` ®â«¨ç ¥âáï ®â `toggle_wrap()` ⥬, çâ® �… ¯¥à¥áâà ¨¢ ¥â
|
|
||||||
`line_offset[]` (wrap-ᥣ¬¥â æ¨ï ¥ ¬¥ï¥âáï), ⮫쪮 ¯¥à¥à¥¤¥à¨â íªà .
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## API-®¢¨ª¨ ¢ libc (¬¨¨¬ «ìë¥)
|
|
||||||
|
|
||||||
### `getkey()` - extended key reader (¢ libc)
|
|
||||||
|
|
||||||
’¥ªã騩 `getch()` â¥àï¥â scan code à áè¨à¥ëå ª« ¢¨è (¢®§¢à é ¥â ⮫쪮 E=ASCII). „®¡ ¢«ï¥¬ **áà §ã ¢ `libc/conio/conio.c`** ®¢ãî äãªæ¨î à冷¬ á `getch()`:
|
|
||||||
|
|
||||||
```c
|
|
||||||
// Returns scan in high byte, ASCII in low byte.
|
|
||||||
// Extended keys (arrows, F-keys, PgUp/PgDn, Home/End): ASCII=0, scan code ¢ high byte.
|
|
||||||
// Plain keys: ASCII ¢ low byte; high byte ᮤ¥à¦¨â positional scan (¡¨â 7 = Ctrl/Alt/Shift modifier).
|
|
||||||
uint16_t getkey(void) __naked {
|
|
||||||
__asm
|
|
||||||
ld c, #0x30 ; ESTEX WAITKEY
|
|
||||||
rst #0x10 ; A=ASCII, D=scan, E=ASCII
|
|
||||||
ld e, a ; ensure E=ASCII even if E clobbered
|
|
||||||
ret ; SDCC __sdcccall(1): ¢®§¢à â uint16_t ¢ DE (D=scan, E=ASCII)
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
„®¯®«¨â¥«ì® - **¢ `libc/include/conio.h`** ¯à®¯¨á âì ¯à®â®â¨¯ ¨ ª®áâ âë scan-ª®¤®¢:
|
|
||||||
|
|
||||||
```c
|
|
||||||
uint16_t getkey(void);
|
|
||||||
|
|
||||||
/* Scan codes for getkey() high byte when ASCII=0 (extended keys). */
|
|
||||||
#define KEY_F1 0x0E
|
|
||||||
#define KEY_F2 0x0F
|
|
||||||
#define KEY_F3 0x10
|
|
||||||
#define KEY_F4 0x11
|
|
||||||
#define KEY_F5 0x12
|
|
||||||
#define KEY_F6 0x13
|
|
||||||
#define KEY_F7 0x14
|
|
||||||
#define KEY_F8 0x15
|
|
||||||
#define KEY_F9 0x16
|
|
||||||
#define KEY_F10 0x17
|
|
||||||
#define KEY_F11 0x18
|
|
||||||
#define KEY_F12 0x19
|
|
||||||
#define KEY_END 0x24
|
|
||||||
#define KEY_DOWN 0x25
|
|
||||||
#define KEY_PGDN 0x26
|
|
||||||
#define KEY_LEFT 0x27
|
|
||||||
#define KEY_RIGHT 0x29
|
|
||||||
#define KEY_HOME 0x2A
|
|
||||||
#define KEY_UP 0x2B
|
|
||||||
#define KEY_PGUP 0x2C
|
|
||||||
#define KEY_INS 0x23
|
|
||||||
#define KEY_DEL 0x22
|
|
||||||
```
|
|
||||||
|
|
||||||
**‘ª -ª®¤ë (¨§ docs/converted/ProgrammerManual.txt:2143-2323):**
|
|
||||||
| Š« ¢¨è | scan | Š« ¢¨è | scan |
|
|
||||||
|---|---|---|---|
|
|
||||||
| F1 | 0x0E | Up | 0x2B |
|
|
||||||
| F10 | 0x17 | Down | 0x25 |
|
|
||||||
| F11 | 0x18 | Left | 0x27 |
|
|
||||||
| F12 | 0x19 | Right | 0x29 |
|
|
||||||
| | | PgUp | 0x2C |
|
|
||||||
| | | PgDn | 0x26 |
|
|
||||||
| | | Home | 0x2A |
|
|
||||||
| | | End | 0x24 |
|
|
||||||
|
|
||||||
### —â® **¯¥à¥¨á¯®«ì§ã¥¬** ¨§ áãé¥áâ¢ãî饩 libc
|
|
||||||
|
|
||||||
- `open/read/lseek/close` - `libc/io/{open,read,lseek}.c` (POSIX wrappers)
|
|
||||||
- `mem_alloc_pages/mem_free_block/mem_get_page` - `libc/mem/mem_alloc.c`
|
|
||||||
- `sprinter_page_w3()` - inline `__sfr` write ¢ `libc/include/sprinter.h:113`
|
|
||||||
- `bank_read_w3/bank_write_w3` - `libc/mem/bank_io_w3.c` (¤«ï fallback ¨«¨ v2 multi-page)
|
|
||||||
- `wrchar(x, y, ch, attr)` - `libc/conio/conio.c:476` (¡¥§ auto-scroll, ¨¤¥ «ì® ¤«ï viewport)
|
|
||||||
- `clrscr_attr(attr)` - `libc/conio/conio.c:395`
|
|
||||||
- `gotoxy/wherex/wherey` - `libc/conio/conio.c:412-462` (¥á«¨ 㦮)
|
|
||||||
- `kbhit()` - `libc/conio/conio.c:22` (¤«ï non-blocking ®¯à®á , ®¯æ¨® «ì®)
|
|
||||||
- `dec16/dec8` - `libc/stdio/dec_print.c` (¤«ï status bar: ⥪ãé ï áâப / total / %)
|
|
||||||
- `COLOR(fg, bg)` ¬ ªà®á - `libc/include/conio.h:152`
|
|
||||||
- –¢¥â®¢ë¥ ª®áâ âë `COLOR_*` - `libc/include/conio.h:145`
|
|
||||||
- `strlen/memcpy/memset` - z80.lib (�… ¯¥à¥¯¨áë¢ âì)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ‘âàãªâãà ¨á室¨ª®¢
|
|
||||||
|
|
||||||
```
|
|
||||||
examples/mdview/
|
|
||||||
ÃÄÄ Makefile # áâ ¤ àâë© pattern (á¬. examples/cat/Makefile)
|
|
||||||
ÃÄÄ mdview.c # Phase 1: ¢áñ ¢ ®¤®¬ ä ©«¥ (main, keys, indexing, render)
|
|
||||||
ÃÄÄ SAMPLE.MD # â¥áâ®¢ë© markdown
|
|
||||||
ÀÄÄ README.md # ®¯¨á ¨¥ ¨ controls
|
|
||||||
```
|
|
||||||
|
|
||||||
�®á«¥ Phase 3 à ᪨¤ âì ¯® ¬®¤ã«ï¬ (¥á«¨ á㬬 àë© à §¬¥à > ~6KB):
|
|
||||||
```
|
|
||||||
mdview.c - main loop, status/menu bars, key dispatch
|
|
||||||
mdrender.c - line rendering with MD inline parser
|
|
||||||
mdindex.c - file load + line indexing
|
|
||||||
```
|
|
||||||
|
|
||||||
### ‘¡®àª
|
|
||||||
|
|
||||||
```makefile
|
|
||||||
PROJ ?= ../..
|
|
||||||
SPRINTER_CC := $(PROJ)/bin/sprinter-cc
|
|
||||||
mdview.exe: mdview.c
|
|
||||||
$(SPRINTER_CC) --memory small -o $@ mdview.c
|
|
||||||
```
|
|
||||||
|
|
||||||
`--memory small`: ª®¤+¤ ë¥ ¢ W2 (DSS ¤ ñâ 㦮¥ ç¨á«® áâà ¨æ); ä ©« - ®â¤¥«ì ï EMM-áâà ¨æ ¢ W3.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ‘âàãªâãà mdview.c (Phase 1, í᪨§)
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sprinter.h>
|
|
||||||
#include <sprinter_mem.h>
|
|
||||||
|
|
||||||
#define VIEW_TOP 1
|
|
||||||
#define VIEW_BOT 30 // inclusive
|
|
||||||
#define VIEW_H 30
|
|
||||||
#define SCREEN_W 80
|
|
||||||
#define MAX_LINES 2048
|
|
||||||
#define FILE_BUF ((char*)0xC000) /* W3 - EMM page mapped here */
|
|
||||||
#define TAB_STOP 4
|
|
||||||
|
|
||||||
#define ATTR_TEXT COLOR(COLOR_WHITE, COLOR_BLACK)
|
|
||||||
#define ATTR_BAR COLOR(COLOR_WHITE, COLOR_BLUE)
|
|
||||||
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLUE)
|
|
||||||
#define ATTR_MENU_T COLOR(COLOR_WHITE, COLOR_BLUE)
|
|
||||||
|
|
||||||
static uint16_t line_off[MAX_LINES];
|
|
||||||
static uint16_t n_lines;
|
|
||||||
static uint16_t top_line;
|
|
||||||
static uint16_t file_size;
|
|
||||||
static uint8_t file_blk;
|
|
||||||
static char filename[64];
|
|
||||||
|
|
||||||
static int load_file(const char *path); // open, alloc EMM page, map W3, read, close
|
|
||||||
static void index_lines(void); // scan FILE_BUF, fill line_off[]
|
|
||||||
static void render_status(void); // row 0
|
|
||||||
static void render_menu(void); // row 31
|
|
||||||
static void render_line(uint16_t idx, uint8_t row); // one line @ row
|
|
||||||
static void render_viewport(void); // VIEW_H lines starting from top_line
|
|
||||||
static void scroll_up(uint16_t n);
|
|
||||||
static void scroll_down(uint16_t n);
|
|
||||||
static void help_screen(void); // F1
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if (argc < 2) { puts("Usage: mdview <file.md>"); return 1; }
|
|
||||||
if (load_file(argv[1]) < 0) { puts("load error"); return 1; }
|
|
||||||
index_lines();
|
|
||||||
clrscr_attr(ATTR_TEXT);
|
|
||||||
render_menu();
|
|
||||||
render_status();
|
|
||||||
render_viewport();
|
|
||||||
for (;;) {
|
|
||||||
uint16_t k = getkey();
|
|
||||||
uint8_t ascii = k & 0xFF;
|
|
||||||
uint8_t scan = (k >> 8) & 0x7F; // strip mod bit
|
|
||||||
if (ascii) {
|
|
||||||
if (ascii == 0x1B) break; // Esc -> exit
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (scan) {
|
|
||||||
case KEY_F10: goto exit;
|
|
||||||
case KEY_F1: help_screen(); break;
|
|
||||||
case KEY_UP: scroll_up(1); break;
|
|
||||||
case KEY_DOWN: scroll_down(1); break;
|
|
||||||
case KEY_PGUP: scroll_up(VIEW_H); break;
|
|
||||||
case KEY_PGDN: scroll_down(VIEW_H); break;
|
|
||||||
case KEY_HOME: top_line = 0;
|
|
||||||
render_viewport(); break;
|
|
||||||
case KEY_END: /* clamp to last viewport */ break;
|
|
||||||
}
|
|
||||||
render_status();
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
mem_free_block(file_blk);
|
|
||||||
clrscr_attr(ATTR_TEXT);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
### Phase 1
|
|
||||||
1. �®¤£®â®¢¨âì `SAMPLE.MD` ~5 KB (§ £®«®¢ª¨, ¡§ æë, ᯨ᪨) - ।¥à¨âìáï ¡ã¤¥â plain.
|
|
||||||
2. `cd examples/mdview && make`
|
|
||||||
3. `python make_disk.py mdview.exe SAMPLE.MD -> mc.img && ./run_mame.sh`
|
|
||||||
4. �஢¥à¨âì:
|
|
||||||
- Status bar ¯®ª §ë¢ ¥â `SAMPLE.MD L 1-30 / N X%`
|
|
||||||
- Menu bar ¢¨§ã
|
|
||||||
- ^/V: 1 áâப
|
|
||||||
- PgUp/PgDn: 30 áâப, ª®à४⮥ clamp £à ¨æ å
|
|
||||||
- Home: top_line=0
|
|
||||||
- End: top_line = total_lines - VIEW_H
|
|
||||||
- F1: ¯®ª §ë¢ ¥â help, «î¡ ï ª« ¢¨è ¢®§¢à é ¥â
|
|
||||||
- F10 / Esc: ¢ë室, íªà ®ç¨é¥
|
|
||||||
5. Edge cases: ¯ãá⮩ ä ©«, ä ©« ¨§ ®¤®© áâப¨, ä ©« á ®ç¥ì ¤«¨®© áâப®© (>80), CRLF ¨ LF mixed.
|
|
||||||
|
|
||||||
### Phase 2-4
|
|
||||||
� áè¨àïâì `SAMPLE.MD` á ä¨ç ¬¨ ¯® ¬¥à¥ ¤®¡ ¢«¥¨ï, ¢¨§ã «ì® ¢¥à¨ä¨æ¨à®¢ âì ¢ MAME. ‘ªà¨è®â-áà ¢¥¨¥ ®¯æ¨® «ì®.
|
|
||||||
|
|
||||||
### �¥£à¥áᨨ
|
|
||||||
- �¨ª ª¨å ¨§¬¥¥¨© ¢ libc Phase 1 ªà®¬¥ (¯®â¥æ¨ «ì®) ¤®¡ ¢«¥¨ï `getkey()` ¢ `libc/conio/conio.c` - ¥á«¨ â ª, ¯à®£ âì `examples/conio2` ¨ `examples/filetest` ç⮡ë ã¡¥¤¨âìáï çâ® ¨ç¥£® ¥ á«®¬ «®áì.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## �¥è¥¨ï ¯® ¥®¤®§ ç®áâï¬
|
|
||||||
|
|
||||||
1. **Word-wrap vs truncate**: v1 = truncate (¯à®áâ®). v2 - F2 toggle wrap.
|
|
||||||
2. **ƒ®à¨§®â «ìë© áªà®««¨£**: v1 - ¥â; v2 - `<-/->` ᤢ¨£ viewport ¯® á⮫¡æ ¬.
|
|
||||||
3. **Tab handling**: ¯à¥®¡à §®¢ ¨¥ ¯à¨ ।¥à¥, **tabstop = 4** (áâ ¤ àâ MD). Žà¨£¨ « ¢ W3 ¥ âண ¥¬.
|
|
||||||
4. **UTF-8**: ।¥à¨¬ ¡ ©âë ª ª ¥áâì. …᫨ ä ©« ¢ CP866 - ®âà¨áã¥âáï ª¨à¨««¨æ¥© ç¥à¥§ á¨áâ¥¬ë© ä®â. UTF-8 - ¯®ª ¥ ¯®¤¤¥à¦¨¢ ¥¬ (¢¨§ã «ì® ¡ã¤¥â ª è ¥-ASCII ᨬ¢®« å; ¤¥â¥ªâ ¨ ¯à¥¤ã¯à¥¦¤¥¨¥ - ¢ v2).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 6 - �®«ë© layout â ¡«¨æ (deferred)
|
|
||||||
|
|
||||||
‘¥©ç á (Phase 4-tables) â ¡«¨æë à¨áãîâáï "¤¥ª®à ⨢®" - ¯ ©¯ë ¨ â¨à¥
|
|
||||||
§ ¬¥ïîâáï box-drawing chars, ® è¨à¨ë ª®«®®ª ¡¥àãâáï ª ª ¥áâì ¨§
|
|
||||||
¨á室¨ª . –¥«ì Phase 6 - ¯¥à¥áç¨â âì â ¡«¨æã ¢ ®à¬ «¨§®¢ ë© ¢¨¤:
|
|
||||||
|
|
||||||
- **Pre-scan â ¡«¨æë**: ¯à®©â¨ ¢á¥ áâப¨ ®¤®£® table-¡«®ª , ©â¨
|
|
||||||
¬ ªá¨¬ «ìãî è¨à¨ã ª ¦¤®© ª®«®ª¨ (á ãçñ⮬ áꥤ¥ëå inline-¬ થ஢ -
|
|
||||||
¢¨§ã «ìë© à §¬¥à, ¥ ¡ ©â®¢ë©).
|
|
||||||
- **Re-emit ¢ ¡ãä¥à¥**: ¯à¨ § £à㧪¥ ä ©« (¨«¨ ¯à¨ ¯¥à¢®© ¢áâà¥ç¥ â ¡«¨æë)
|
|
||||||
¯¥à¥¯¨á âì áâப¨ ¢ FILE_BUF â ª, çâ®¡ë ¢á¥ ï祩ª¨ ®¤®© ª®«®ª¨ ¨¬¥«¨
|
|
||||||
®¤¨ ª®¢ãî è¨à¨ã; ¤®¡ ¢¨âì top/bottom à ¬ª¨ (`ÚÄÂÄ¿` / `ÀÄÁÄÙ`) ª ª
|
|
||||||
á¨â¥â¨ç¥áª¨¥ áâப¨. �â® ¯®§¢®«¨â á®åà ¨âì 1:1 ᮮ⢥âá⢨¥ "«®£¨ç¥áª ï
|
|
||||||
áâப -> ®¤ viewport row" ¡¥§ á¯¥æ¨ «ì®© «®£¨ª¨ ¯à¨ ।¥à¥.
|
|
||||||
- **� ¬ïâì**: re-emit ¬®¦¥â “‚…‹ˆ—ˆ’œ ä ©« § áçñâ padding ¨ ¤®¯.à ¬®ª.
|
|
||||||
…᫨ ¡ãä¥à ¡«¨§®ª ª 16KB - ®â१ âì â ¡«¨æã ¨ ¯®¬¥â¨âì ¥ñ overflow'®¬.
|
|
||||||
- **ƒ®à¨§®â «ìë© áªà®««¨£**: ¥á«¨ ¨â®£®¢ ï è¨à¨ â ¡«¨æë (¨«¨ «î¡®©
|
|
||||||
áâப¨) > SCREEN_W = 80 - ¤®¡ ¢¨âì <-/-> ¤«ï horizontal pan. �â® ¡ã¤¥â
|
|
||||||
®¡é¨© ¬¥å ¨§¬ ¤«ï ¤«¨ëå áâப (á¬. â ª¦¥ wrap mode), ¥ ⮫쪮 â ¡«¨æ.
|
|
||||||
- **‚ëà ¢¨¢ ¨¥ ¨§ separator-row**: `:-` -> left, `-:` -> right, `:-:` ->
|
|
||||||
center; ãç¨âë¢ âì ¯à¨ padding'¥ ᮤ¥à¦¨¬®£® ï祩ª¨.
|
|
||||||
- **˜ £¨ ॠ«¨§ 樨**:
|
|
||||||
1. Walking pass ¯® ä¥á ¬/â ¡«¨æ ¬ ¯àאַ ¢ `index_lines()` - ᮡà âì
|
|
||||||
extents ¢á¥å â ¡«¨æ.
|
|
||||||
2. „«ï ª ¦¤®© â ¡«¨æë - ®¯à¥¤¥«¨âì è¨à¨ë ª®«®®ª.
|
|
||||||
3. �¥è¥¨¥: rewrite-in-buffer (¯à®é¥ ¤«ï ।¥à , ® ¬ãâ¨àã¥â ¨á室¨ª)
|
|
||||||
vs render-time layout (cleaner, ® âॡã¥â ®â¤¥«ì®© áâàãªâãàë
|
|
||||||
®¯¨á ¨ï layout' ª ¦¤ãî â ¡«¨æã).
|
|
||||||
4. Hpan: ®¡é¨© `viewport_x_offset` ¤«ï ¢á¥£® íªà , ¨«¨ ®â¤¥«ìë©
|
|
||||||
"è¨à®ª¨© ०¨¬" ⮫쪮 ¢ãâਠ⠡«¨æ.
|
|
||||||
|
|
||||||
> �¥ ¡«®ª¨àãîé ï ä¨ç . ‡ ¯ã᪠âì ª®£¤ á⠥⠯®ï⥠⨯®¢®© ¨áâ®ç¨ª
|
|
||||||
> markdown-ä ©«®¢ (㧪¨¥ ç¨â «ª¨ -> ¤®áâ â®ç® ⥪ã饣® ¤¥ª®à â®à ;
|
|
||||||
> è¨à®ª¨¥ README á ¡®«ì訬¨ â ¡«¨æ ¬¨ -> 㦥 ¯®«ë© layout).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase - Šíè ।¥à¥ëå áâப (low priority)
|
|
||||||
|
|
||||||
Žâ«®¦¥®: ⥪ãé ï ᪮à®áâì ¡®«¥¥ 祬 ¤®áâ â®ç . €ªâ¨¢¨à®¢ âì ¥á«¨ ¯®ï¢¨âáï
|
|
||||||
áæ¥ ਩, £¤¥ ¢¨¤ § ¤¥à¦ª PgUp/PgDn ( ¯à¨¬¥à, ¯à¨ âï¦ñ«®¬ inline-¯ àá¥à¥
|
|
||||||
v2 á UTF-8 / linkifier / â ¡«¨æ ¬¨).
|
|
||||||
|
|
||||||
**Šíè ®âä®à¬ â¨à®¢ ëå áâப** (W3, ®â¤¥«ì ï EMM-áâà ¨æ ):
|
|
||||||
|
|
||||||
```
|
|
||||||
Cache layout (16 KB EMM page, ¢á¥£® 16000 ¡ ©â ¨á¯®«ì§ã¥âáï):
|
|
||||||
slot 0: 80 chars + 80 attrs = 160 bytes @ offset 0
|
|
||||||
...
|
|
||||||
slot 99: 80 chars + 80 attrs = 160 bytes @ offset 15840
|
|
||||||
|
|
||||||
Cache tags (W2 static): uint16_t cache_tag[100] = 200 bytes
|
|
||||||
cache_tag[i] = line_id, ¨«¨ 0xFFFF = invalid
|
|
||||||
```
|
|
||||||
|
|
||||||
‘âà ⥣¨ï - **direct map (no LRU)**: `slot = line_id % 100`. Š®««¨§¨ï -> ¢ëâ¥á¥¨¥.
|
|
||||||
|
|
||||||
**Batched viewport render**: 2 page-swap' ‚…‘œ viewport (cache -> file -> cache),
|
|
||||||
¥ 60 ª ª ¯à¨ ¨¢®© ॠ«¨§ 樨.
|
|
||||||
|
|
||||||
�ਠᡮથ: `cache_blk = mem_alloc_pages(1)` ¯®á«¥ `file_blk`; `mem_free_block` exit.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## —â® ®â«®¦¥® ¢ v2
|
|
||||||
|
|
||||||
- ~~” ©«ë >16 KB~~ - **ᤥ« ® ¢ v1.5** (¤® 128 KB ç¥à¥§ 1-8 EMM-áâà ¨æ + lazy map ¢ W3).
|
|
||||||
- ~~Word wrap~~ - **ᤥ« ®** (Phase 5, F2 toggle).
|
|
||||||
- Search (Find / Find next) - F3 / F4.
|
|
||||||
- F8 Raw / Render toggle - ᯥæ¨ä¨ª æ¨ï ¢ Phase 8.
|
|
||||||
- Links `[text](url)` + image alt - Phase 7.
|
|
||||||
- Tables - Phase 6 (¯®«ë© layout).
|
|
||||||
- Toggle highlight on/off - ç áâë© á«ãç © F8 Raw.
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# MDView — Просмотрщик Markdown для Sprinter
|
||||||
|
|
||||||
|
**MDView** — программа для просмотра документов в формате *Markdown* на компьютере
|
||||||
|
Sprinter (процессор Z80). Документ хранится в отдельном W3 окне и не занимает
|
||||||
|
основную RAM программы.
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- Документы до **128 КБ** (8 страниц EMM по 16 КБ каждая)
|
||||||
|
- До **16 384** экранных строк в индексе
|
||||||
|
- Автоматический перенос слов по ширине экрана (80 столбцов)
|
||||||
|
- Горизонтальный сдвиг для широких строк (блоки кода, таблицы)
|
||||||
|
- Статус-бар: имя файла, диапазон строк, процент прокрутки
|
||||||
|
- Спиннер в строке состояния во время загрузки и индексации
|
||||||
|
- Поддержка «мягкого» склеивания строк в абзацах и цитатах
|
||||||
|
|
||||||
|
## Запуск
|
||||||
|
|
||||||
|
```
|
||||||
|
mdview [имя_файла.md]
|
||||||
|
```
|
||||||
|
|
||||||
|
Если имя файла не задано, загружается `README.MD`.
|
||||||
|
|
||||||
|
## Управление
|
||||||
|
|
||||||
|
```
|
||||||
|
Клавиша Действие
|
||||||
|
───────────── ────────────────────────────────────────
|
||||||
|
Up Down Прокрутка на одну строку вверх / вниз
|
||||||
|
PgUp PgDn Прокрутка на страницу (30 строк)
|
||||||
|
Home Начало документа
|
||||||
|
End Конец документа
|
||||||
|
Left Right Горизонтальный сдвиг (только nowrap-строки)
|
||||||
|
F1 Окно справки
|
||||||
|
F10 / Esc Выход из программы
|
||||||
|
```
|
||||||
|
|
||||||
|
## Синтаксис Markdown
|
||||||
|
|
||||||
|
### Заголовки
|
||||||
|
|
||||||
|
Поддерживаются уровни H1–H4. Уровни H5 и H6 отображаются как H4.
|
||||||
|
|
||||||
|
# Заголовок первого уровня
|
||||||
|
## Заголовок второго уровня
|
||||||
|
### Заголовок третьего уровня
|
||||||
|
#### Заголовок четвёртого уровня
|
||||||
|
|
||||||
|
### Текстовое форматирование
|
||||||
|
|
||||||
|
**Жирный текст** выделяется двойными звёздочками: `**текст**`
|
||||||
|
|
||||||
|
*Курсив* выделяется одиночными звёздочками `*текст*` или знаком подчёркивания `_текст_`
|
||||||
|
|
||||||
|
`Встроенный код` обозначается обратными кавычками
|
||||||
|
|
||||||
|
~~Зачёркнутый текст~~ — двойные тильды: `~~текст~~`
|
||||||
|
|
||||||
|
### Ненумерованный список
|
||||||
|
|
||||||
|
Маркеры `-`, `*` или `+`:
|
||||||
|
|
||||||
|
- Первый пункт списка
|
||||||
|
- Второй пункт списка
|
||||||
|
- Третий пункт с достаточно длинным текстом, который при необходимости
|
||||||
|
будет перенесён на следующую строку с сохранением отступа
|
||||||
|
|
||||||
|
### Нумерованный список
|
||||||
|
|
||||||
|
1. Первый элемент
|
||||||
|
2. Второй элемент
|
||||||
|
3. Третий элемент
|
||||||
|
|
||||||
|
### Цитата
|
||||||
|
|
||||||
|
> Блок цитаты начинается с символа `>`. Несколько последовательных
|
||||||
|
> строк одной цитаты склеиваются в единый абзац с автоматическим
|
||||||
|
> переносом слов.
|
||||||
|
|
||||||
|
### Блок кода (verbatim)
|
||||||
|
|
||||||
|
Блок кода заключается в тройные обратные кавычки. Внутри блока
|
||||||
|
текст отображается «как есть» без разбора Markdown:
|
||||||
|
|
||||||
|
```
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sprinter.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
puts("Hello, Sprinter!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Горизонтальная линия
|
||||||
|
|
||||||
|
Три или более символов `---`, `***` или `___` на отдельной строке:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Технические характеристики
|
||||||
|
|
||||||
|
- **Платформа:** Sprinter, процессор Z80 @ 21 МГц
|
||||||
|
- **Кодировка:** CP866 (DOS Cyrillic)
|
||||||
|
- **Максимальный размер файла:** 128 КБ
|
||||||
|
- **Максимальное число строк в индексе:** 16 384
|
||||||
|
- **Режим памяти:** small
|
||||||
|
- Код программы, cтек, данные, куча — окнa W1-W2 (32 КБ, адреса 0x4000–0xBFFF).
|
||||||
|
- Буфер файла — страницы EMM, отображаемые в W3 (0xC000–0xFFFF)
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
1. **Увеличение размера документов.** Снять лимит 128 КБ: Достаточно
|
||||||
|
разрешить работать с большим кол-вом страниц памяти, пока оттестированно
|
||||||
|
на работе с 8-мю страницами по 16Кб.
|
||||||
|
|
||||||
|
2. **Форматированные таблицы.** Разбирать строки вида `| ячейка | ячейка |`
|
||||||
|
с автоматическим выравниванием столбцов и отрисовкой разделительных
|
||||||
|
линий (строки `|---|---|`). На текущий момент таблицы отображаются
|
||||||
|
как обычные nowrap-строки без выравнивания.
|
||||||
|
|
||||||
|
3. **Поддержка кодировок CP1251 и UTF-8.** Автоопределение кодировки
|
||||||
|
по BOM, явное указание через аргумент командной строки (`--encoding cp1251`),
|
||||||
|
возможность переключения кодировки во время просмотра (`F8`).
|
||||||
|
Нужно прежде всего для документов на русском языке; CP866 кодировака уже поддерживается.
|
||||||
|
|
||||||
|
4. **Ускорение рендеринга.** Кэш строк экрана. Оптимизация цикла вывода
|
||||||
|
символов через BIOS WRCHAR (пакетный вывод, DMA).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*MDView v0.2 · (c) 2026 Петров А.Г.*
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
MDVIEW Sample Document
|
|
||||||
|
|
||||||
This is a sample Markdown file for testing the Sprinter *mdview text
|
|
||||||
viewer*. Phase 3 adds inline emphasis: bold, italic and underscore
|
|
||||||
runs render with distinct background colours.
|
|
||||||
+112
-2
@@ -87,6 +87,20 @@ static const uint8_t md_pallete[16 * 4] = {
|
|||||||
#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
|
#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
|
||||||
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK)
|
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK)
|
||||||
|
|
||||||
|
/* ---- Атрибуты диалога справки ------------------------------------ */
|
||||||
|
|
||||||
|
#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
|
||||||
|
#define ATTR_HELP_BDR COLOR(COLOR_WHITE, COLOR_BLACK)
|
||||||
|
#define ATTR_HELP_TIT COLOR(COLOR_YELLOW, COLOR_BLACK)
|
||||||
|
#define ATTR_HELP_HDR COLOR(COLOR_WHITE, COLOR_BLACK)
|
||||||
|
#define ATTR_HELP_HINT COLOR(COLOR_YELLOW, COLOR_BLACK)
|
||||||
|
|
||||||
|
/* Геометрия диалога справки (в символьных координатах 80×32). */
|
||||||
|
#define HELP_X 8u /* левая граница рамки */
|
||||||
|
#define HELP_Y 4u /* верхняя граница рамки */
|
||||||
|
#define HELP_W 64u /* ширина рамки (включая │) */
|
||||||
|
#define HELP_H 22u /* высота рамки (включая ─) */
|
||||||
|
|
||||||
/* ---- Глобальное состояние ---------------------------------------- */
|
/* ---- Глобальное состояние ---------------------------------------- */
|
||||||
|
|
||||||
/* Файл до 128 КБ хранится в наборе EMM-страниц (до 8 шт. по 16 КБ).
|
/* Файл до 128 КБ хранится в наборе EMM-страниц (до 8 шт. по 16 КБ).
|
||||||
@@ -1662,6 +1676,86 @@ static void die(const char *msg)
|
|||||||
(void)getkey();
|
(void)getkey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================================================================
|
||||||
|
* Диалог справки (F1)
|
||||||
|
* ================================================================== */
|
||||||
|
|
||||||
|
/* Заполняет одну внутреннюю строку диалога (r=0 — первая строка за рамкой).
|
||||||
|
* Строка s в кодировке CP866; остаток до края дополняется пробелами. */
|
||||||
|
static void help_line(uint8_t r, const char *s, uint8_t attr)
|
||||||
|
{
|
||||||
|
uint8_t x = HELP_X + 1u;
|
||||||
|
uint8_t y = HELP_Y + 1u + r;
|
||||||
|
uint8_t i = 0;
|
||||||
|
while (s[i] && i < HELP_W - 2u) {
|
||||||
|
wrchar((uint8_t)(x + i), y, s[i], attr);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while (i < HELP_W - 2u) {
|
||||||
|
wrchar((uint8_t)(x + i), y, ' ', ATTR_HELP_BG);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_help(void)
|
||||||
|
{
|
||||||
|
/* " Помощь " в CP866 (8 байт) */
|
||||||
|
static const char title[] = " \x8F\xAE\xAC\xAE\xE9\xEC ";
|
||||||
|
uint8_t tlen = 8u;
|
||||||
|
uint8_t lft = (uint8_t)((HELP_W - 2u - tlen) / 2u); /* = 27 */
|
||||||
|
uint8_t rgt = (uint8_t)(HELP_W - 2u - tlen - lft);
|
||||||
|
uint8_t cx, i;
|
||||||
|
|
||||||
|
/* Верхняя граница рамки с заголовком по центру */
|
||||||
|
wrchar(HELP_X, HELP_Y, 0xDA, ATTR_HELP_BDR); /* ┌ */
|
||||||
|
cx = HELP_X + 1u;
|
||||||
|
for (i = 0; i < lft; i++, cx++) wrchar(cx, HELP_Y, 0xC4, ATTR_HELP_BDR);
|
||||||
|
for (i = 0; i < tlen; i++, cx++) wrchar(cx, HELP_Y, title[i], ATTR_HELP_TIT);
|
||||||
|
for (i = 0; i < rgt; i++, cx++) wrchar(cx, HELP_Y, 0xC4, ATTR_HELP_BDR);
|
||||||
|
wrchar(HELP_X + HELP_W - 1u, HELP_Y, 0xBF, ATTR_HELP_BDR); /* ┐ */
|
||||||
|
|
||||||
|
/* Боковые границы (левый и правый │ для каждой строки тела) */
|
||||||
|
for (uint8_t r = 1u; r < HELP_H - 1u; r++) {
|
||||||
|
wrchar(HELP_X, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */
|
||||||
|
wrchar(HELP_X + HELP_W - 1u, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Нижняя граница рамки */
|
||||||
|
wrchar(HELP_X, HELP_Y + HELP_H - 1u, 0xC0, ATTR_HELP_BDR); /* └ */
|
||||||
|
for (i = 1u; i < HELP_W - 1u; i++)
|
||||||
|
wrchar(HELP_X + i, HELP_Y + HELP_H - 1u, 0xC4, ATTR_HELP_BDR);
|
||||||
|
wrchar(HELP_X + HELP_W - 1u, HELP_Y + HELP_H - 1u, 0xD9, ATTR_HELP_BDR); /* ┘ */
|
||||||
|
|
||||||
|
/* Содержимое (20 внутренних строк) */
|
||||||
|
uint8_t r = 0;
|
||||||
|
help_line(r++, "", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " MDView v0.2 -- Markdown Viewer for Sprinter", ATTR_HELP_HDR);
|
||||||
|
help_line(r++, " (c) 2026 Petrov A.G.", ATTR_HELP_BG);
|
||||||
|
help_line(r++, "", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " Navigation:", ATTR_HELP_HDR);
|
||||||
|
help_line(r++, " \x18 \x19 Scroll one line up / down", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " PgUp PgDn Scroll one page up / down", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " Home End Jump to beginning / end of document", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " \x1B \x1A Horizontal pan (code blocks/tables)", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " Esc F10 Exit", ATTR_HELP_BG);
|
||||||
|
help_line(r++, "", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " Markdown elements:", ATTR_HELP_HDR);
|
||||||
|
help_line(r++, " # ## ### Headings H1-H6", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " **bold** *italic* `code` ~~strike~~", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " > quote ``` ... ``` Fenced code block", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " - * + item 1. 2. Ordered list", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " |----|----| Tables", ATTR_HELP_BG);
|
||||||
|
help_line(r++, "", ATTR_HELP_BG);
|
||||||
|
help_line(r++, " File size: up to 128 KB (EMM). Lines: up to 16384.", ATTR_HELP_BG);
|
||||||
|
help_line(r++, "", ATTR_HELP_BG);
|
||||||
|
|
||||||
|
(void)getkey();
|
||||||
|
|
||||||
|
render_full_status();
|
||||||
|
render_viewport();
|
||||||
|
render_menu();
|
||||||
|
}
|
||||||
|
|
||||||
/* ==================================================================
|
/* ==================================================================
|
||||||
* Точка входа
|
* Точка входа
|
||||||
* ================================================================== */
|
* ================================================================== */
|
||||||
@@ -1669,8 +1763,23 @@ static void die(const char *msg)
|
|||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *path;
|
const char *path;
|
||||||
if (argc >= 2) path = argv[1];
|
if (argc >= 2) {
|
||||||
else path = "SAMPLE.MD";
|
path = argv[1];
|
||||||
|
} else {
|
||||||
|
path = "README.MD";
|
||||||
|
/* Дефолтный файл отсутствует — выводим подсказку без смены режима экрана. */
|
||||||
|
{
|
||||||
|
int chk = open(path, O_RDONLY);
|
||||||
|
if (chk < 0) {
|
||||||
|
cputs("MDView v0.2 -- Markdown Viewer for Sprinter\r\n");
|
||||||
|
cputs("(c) 2026 Petrov A.G.\r\n\r\n");
|
||||||
|
cputs("Usage: mdview <file.md>\r\n");
|
||||||
|
cputs(" Displays a Markdown document (CP866, up to 128 KB).\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
close(chk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Копируем путь в static filename[] для отображения в статус-баре. */
|
/* Копируем путь в static filename[] для отображения в статус-баре. */
|
||||||
{
|
{
|
||||||
@@ -1730,6 +1839,7 @@ int main(int argc, char **argv)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (scan) {
|
switch (scan) {
|
||||||
|
case KEY_F1: show_help(); break;
|
||||||
case KEY_F10: goto exit_loop;
|
case KEY_F10: goto exit_loop;
|
||||||
case KEY_UP: scroll_up(1); break;
|
case KEY_UP: scroll_up(1); break;
|
||||||
case KEY_DOWN: scroll_down(1); break;
|
case KEY_DOWN: scroll_down(1); break;
|
||||||
|
|||||||
+5
-1
@@ -25,8 +25,10 @@ LIBC_C := \
|
|||||||
libc/sys/atexit.c \
|
libc/sys/atexit.c \
|
||||||
libc/conio/conio.c \
|
libc/conio/conio.c \
|
||||||
libc/conio/cprintf.c \
|
libc/conio/cprintf.c \
|
||||||
|
libc/conio/text_palette.c \
|
||||||
libc/io/dir.c \
|
libc/io/dir.c \
|
||||||
libc/video/videomode_raw.c \
|
libc/video/videomode_raw.c \
|
||||||
|
libc/video/palette.c \
|
||||||
libc/errno/_errno_set.c \
|
libc/errno/_errno_set.c \
|
||||||
libc/env/env.c \
|
libc/env/env.c \
|
||||||
libc/errno/errno.c \
|
libc/errno/errno.c \
|
||||||
@@ -42,8 +44,10 @@ LIBC_C := \
|
|||||||
libc/io/stat.c \
|
libc/io/stat.c \
|
||||||
libc/mem/bank_io_w3.c \
|
libc/mem/bank_io_w3.c \
|
||||||
libc/mem/bank_io_w1.c \
|
libc/mem/bank_io_w1.c \
|
||||||
libc/mem/mem_alloc.c \
|
libc/mem/mem_estex.c \
|
||||||
|
libc/mem/mem_bios.c \
|
||||||
libc/gfx/gfx_core.c \
|
libc/gfx/gfx_core.c \
|
||||||
|
libc/gfx/gfx_palette.c \
|
||||||
libc/gfx/gfx_raw_common.c \
|
libc/gfx/gfx_raw_common.c \
|
||||||
libc/gfx/gfx_raw_256.c \
|
libc/gfx/gfx_raw_256.c \
|
||||||
libc/gfx/gfx_raw_16.c \
|
libc/gfx/gfx_raw_16.c \
|
||||||
|
|||||||
@@ -356,6 +356,11 @@ char cputs(const char *s) __naked
|
|||||||
or a, l
|
or a, l
|
||||||
ret z
|
ret z
|
||||||
|
|
||||||
|
;; IX is callee-saved under SDCC's z80 ABI. We use it as a
|
||||||
|
;; function pointer below (ld ix, #__raw_putch_raw0/1) so save
|
||||||
|
;; the caller's value up front and restore before every ret.
|
||||||
|
push ix
|
||||||
|
|
||||||
;; KEEP_EXIST_ATTR? high byte of g_text_attr != 0
|
;; KEEP_EXIST_ATTR? high byte of g_text_attr != 0
|
||||||
ld a, (_g_text_attr + 1)
|
ld a, (_g_text_attr + 1)
|
||||||
or a, a
|
or a, a
|
||||||
@@ -398,11 +403,13 @@ char cputs(const char *s) __naked
|
|||||||
_cputs_loop_end:
|
_cputs_loop_end:
|
||||||
|
|
||||||
call __set_cursor
|
call __set_cursor
|
||||||
|
pop ix ; restore caller's IX
|
||||||
xor a, a ; return 0
|
xor a, a ; return 0
|
||||||
ret
|
ret
|
||||||
|
|
||||||
_cputs_fast:
|
_cputs_fast:
|
||||||
call __cputs_pchars
|
call __cputs_pchars
|
||||||
|
pop ix ; restore caller's IX
|
||||||
xor a, a ; return 0
|
xor a, a ; return 0
|
||||||
ret
|
ret
|
||||||
__endasm;
|
__endasm;
|
||||||
|
|||||||
+4
-41
@@ -13,7 +13,7 @@
|
|||||||
* gfx_set_draw_page / get — updates _gfx_addr_base for the new page
|
* gfx_set_draw_page / get — updates _gfx_addr_base for the new page
|
||||||
* gfx_set_bank / get — sets the W3 page byte (0x50..0x5F)
|
* gfx_set_bank / get — sets the W3 page byte (0x50..0x5F)
|
||||||
* gfx_wait_vsync — EI; HALT until next frame interrupt
|
* gfx_wait_vsync — EI; HALT until next frame interrupt
|
||||||
* gfx_pal_load / gfx_pal_set — BIOS $A4 PIC_SET_PAL wrappers
|
* (palette wrappers live in gfx_palette.c, backed by libc/video/palette.c)
|
||||||
*
|
*
|
||||||
* Shared state (extern from this file):
|
* Shared state (extern from this file):
|
||||||
* _gfx_addr_base — 0xC000 for page 0, 0xC140 for page 1. Every
|
* _gfx_addr_base — 0xC000 for page 0, 0xC140 for page 1. Every
|
||||||
@@ -136,43 +136,6 @@ void gfx_wait_vsync(void) __naked
|
|||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Palette (BIOS $A4 PIC_SET_PAL) ----------------------------- */
|
/* Palette wrappers (gfx_pal_load / gfx_pal_set / gfx_pal_get /
|
||||||
|
* gfx_pal_get_color / gfx_pal_reset) moved to gfx_palette.c — see also
|
||||||
static uint8_t pal_num_;
|
* libc/video/palette.c for the shared low-level $A4 / $A6 implementation. */
|
||||||
static uint8_t pal_start_;
|
|
||||||
static uint8_t pal_count_;
|
|
||||||
static uint16_t pal_data_;
|
|
||||||
|
|
||||||
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
|
||||||
const uint8_t *data)
|
|
||||||
{
|
|
||||||
pal_num_ = pal_num;
|
|
||||||
pal_start_ = start;
|
|
||||||
pal_count_ = count;
|
|
||||||
pal_data_ = (uint16_t)(uintptr_t)data;
|
|
||||||
|
|
||||||
__asm
|
|
||||||
push ix
|
|
||||||
ld a, (_pal_start_)
|
|
||||||
ld e, a ; E = start
|
|
||||||
ld a, (_pal_count_)
|
|
||||||
ld d, a ; D = count (0 → 256)
|
|
||||||
ld hl, (_pal_data_) ; HL = data
|
|
||||||
ld b, #0xFF ; mask
|
|
||||||
ld a, (_pal_num_) ; A = palette number
|
|
||||||
ld c, #0xA4 ; BIOS PIC_SET_PAL
|
|
||||||
rst #0x08
|
|
||||||
pop ix
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gfx_pal_set(uint8_t pal_num, uint8_t idx,
|
|
||||||
uint8_t r, uint8_t g, uint8_t b)
|
|
||||||
{
|
|
||||||
uint8_t entry[4];
|
|
||||||
entry[0] = b;
|
|
||||||
entry[1] = g;
|
|
||||||
entry[2] = r;
|
|
||||||
entry[3] = 0;
|
|
||||||
gfx_pal_load(pal_num, idx, 1, entry);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -201,4 +201,36 @@ enum {
|
|||||||
#define COLOR_BLINK 0x80u
|
#define COLOR_BLINK 0x80u
|
||||||
#define COLOR(fg, bg) ((uint8_t)((((bg) & 0x07) << 4) | ((fg) & 0x0F)))
|
#define COLOR(fg, bg) ((uint8_t)((((bg) & 0x07) << 4) | ((fg) & 0x0F)))
|
||||||
|
|
||||||
|
/* Text-mode palette. The 16 logical CGA colours seen by COLOR(fg, bg)
|
||||||
|
* actually live in four 256-entry hardware palette planes indexed by the
|
||||||
|
* full 8-bit attribute byte:
|
||||||
|
*
|
||||||
|
* TEXT_PAL_PAPER — background colour, non-blink phase
|
||||||
|
* TEXT_PAL_INK — foreground colour, non-blink phase
|
||||||
|
* TEXT_PAL_BLINK_PAPER — background colour during the blink half-cycle
|
||||||
|
* TEXT_PAL_BLINK_INK — foreground colour during the blink half-cycle
|
||||||
|
*
|
||||||
|
* For non-blinking attributes (bit 7 = 0) all four planes display the
|
||||||
|
* same colours, so writing to PAPER/INK is enough. For blinking attrs
|
||||||
|
* (bit 7 = 1) the renderer alternates between the non-blink and blink
|
||||||
|
* planes — that's how flash is implemented in hardware.
|
||||||
|
*
|
||||||
|
* These wrappers add 4 to the plane index and forward to the low-level
|
||||||
|
* <palette.h> API (pal_load / pal_set_color / pal_get / pal_get_color).
|
||||||
|
* Use text_pal_reset() to restore the system default CGA palette. */
|
||||||
|
#define TEXT_PAL_PAPER 0
|
||||||
|
#define TEXT_PAL_INK 1
|
||||||
|
#define TEXT_PAL_BLINK_PAPER 2
|
||||||
|
#define TEXT_PAL_BLINK_INK 3
|
||||||
|
|
||||||
|
void text_pal_load (uint8_t plane, uint8_t start, uint8_t count,
|
||||||
|
const uint8_t *bgr0);
|
||||||
|
void text_pal_set_color(uint8_t plane, uint8_t attr,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void text_pal_get (uint8_t plane, uint8_t start, uint8_t count,
|
||||||
|
uint8_t *bgr0);
|
||||||
|
void text_pal_get_color(uint8_t plane, uint8_t attr,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b);
|
||||||
|
void text_pal_reset (void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -145,4 +145,15 @@ void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
|||||||
void gfx_pal_set (uint8_t pal_num, uint8_t idx,
|
void gfx_pal_set (uint8_t pal_num, uint8_t idx,
|
||||||
uint8_t r, uint8_t g, uint8_t b);
|
uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
|
||||||
|
/* Read a contiguous block of entries back from a graphics palette. */
|
||||||
|
void gfx_pal_get (uint8_t pal_num, uint8_t start, uint8_t count,
|
||||||
|
uint8_t *data);
|
||||||
|
|
||||||
|
/* Read one entry into R, G, B pointers (any may be NULL). */
|
||||||
|
void gfx_pal_get_color(uint8_t pal_num, uint8_t idx,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b);
|
||||||
|
|
||||||
|
/* Restore the system default graphics palette (BIOS $A6, type=1). */
|
||||||
|
void gfx_pal_reset(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -47,23 +47,47 @@
|
|||||||
* n : 1..255
|
* n : 1..255
|
||||||
* ret : blk_id (1..255) on success; 0 on failure with errno set.
|
* ret : blk_id (1..255) on success; 0 on failure with errno set.
|
||||||
* The id is opaque — pass it to mem_get_page() and mem_free_block(). */
|
* The id is opaque — pass it to mem_get_page() and mem_free_block(). */
|
||||||
uint8_t mem_alloc_pages(uint8_t n);
|
uint8_t mem_alloc_pages_estex(uint8_t n);
|
||||||
|
uint8_t mem_alloc_pages_bios(uint8_t n);
|
||||||
|
|
||||||
/* Release a block previously returned by mem_alloc_pages().
|
/* Release a block previously returned by mem_alloc_pages().
|
||||||
* On error errno is set (e.g. EINVAL for unknown id). Double-free is
|
* On error errno is set (e.g. EINVAL for unknown id). Double-free is
|
||||||
* NOT idempotent: the second call sets errno. */
|
* NOT idempotent: the second call sets errno. */
|
||||||
void mem_free_block(uint8_t blk_id);
|
void mem_free_block_estex(uint8_t blk_id);
|
||||||
|
void mem_free_block_bios(uint8_t blk_id);
|
||||||
|
|
||||||
/* Translate (block, page-index) into a physical page number suitable
|
/* Translate (block, page-index) into a physical page number suitable
|
||||||
* for sprinter_page_w1/w2/w3() or the bank_*() helpers below.
|
* for sprinter_page_w1/w2/w3() or the bank_*() helpers below.
|
||||||
* blk_id: from mem_alloc_pages()
|
* blk_id: from mem_alloc_pages()
|
||||||
* idx : 0..(n-1)
|
* idx : 0..(n-1)
|
||||||
* ret : physical page (1..255) on success; 0 on failure (errno set). */
|
* ret : physical page (1..255) on success; 0 on failure (errno set). */
|
||||||
uint8_t mem_get_page(uint8_t blk_id, uint8_t idx);
|
uint8_t mem_get_page_bios(uint8_t blk_id, uint8_t idx);
|
||||||
|
|
||||||
/* Query the EMM allocator state. Both pointers must be non-NULL.
|
/* Query the EMM allocator state. Both pointers must be non-NULL.
|
||||||
* Cannot fail (no error path). */
|
* Cannot fail (no error path). */
|
||||||
void mem_info(uint16_t *total, uint16_t *free_pages);
|
void mem_info_estex(uint16_t *total, uint16_t *free_pages);
|
||||||
|
void mem_info_bios(uint16_t *total, uint16_t *free_pages);
|
||||||
|
|
||||||
|
#define MEM_MANAGE_MODE_BIOS
|
||||||
|
|
||||||
|
#ifdef MEM_MANAGE_MODE_ESTEX
|
||||||
|
|
||||||
|
#define mem_alloc_pages mem_alloc_pages_estex
|
||||||
|
#define mem_free_block mem_free_block_estex
|
||||||
|
#define mem_info mem_info_estex
|
||||||
|
|
||||||
|
#define mem_get_page mem_get_page_bios
|
||||||
|
|
||||||
|
#elif defined MEM_MANAGE_MODE_BIOS
|
||||||
|
|
||||||
|
#define mem_alloc_pages mem_alloc_pages_bios
|
||||||
|
#define mem_free_block mem_free_block_bios
|
||||||
|
#define mem_info mem_info_bios
|
||||||
|
|
||||||
|
#define mem_get_page mem_get_page_bios
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
* Far-page accessors via window 3 (base 0xC000, port 0xE2)
|
* Far-page accessors via window 3 (base 0xC000, port 0xE2)
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
/*
|
|
||||||
* mem_alloc_pages / mem_free_block / mem_get_page / mem_info — ESTEX EMM
|
|
||||||
* wrappers for explicit 16 KB-page allocation.
|
|
||||||
*
|
|
||||||
* ESTEX $3C INFOMEM → HL=total pages, BC=free pages
|
|
||||||
* ESTEX $3D GETMEM B=npages → A=block id, CF=err
|
|
||||||
* ESTEX $3E FREEMEM A=block id → CF=err
|
|
||||||
* BIOS $C4 EMM_GETPAGE A=blk, B=idx → A=physical page CF=err
|
|
||||||
*
|
|
||||||
* Pattern: every RST 10h / RST 8 is bracketed with push/pop IX because
|
|
||||||
* ESTEX/BIOS clobber it and the C caller uses it as a frame pointer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sprinter_mem.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate `n` contiguous 16-KB physical pages from the EMM pool.
|
|
||||||
*
|
|
||||||
* in: n — 1..255 (number of pages requested).
|
|
||||||
* out: blk_id (1..255) on success; 0 on failure with errno set.
|
|
||||||
*
|
|
||||||
* The returned block id is opaque — pass it to mem_get_page() to obtain
|
|
||||||
* each physical-page number, and to mem_free_block() when done. Block
|
|
||||||
* ids start at 1; id 0 is reserved as the "allocation failed" sentinel.
|
|
||||||
*/
|
|
||||||
uint8_t mem_alloc_pages(uint8_t n) __naked
|
|
||||||
{
|
|
||||||
(void)n;
|
|
||||||
__asm
|
|
||||||
;; SDCC single-uint8 arg → A on entry; ESTEX GETMEM wants n in B.
|
|
||||||
push ix
|
|
||||||
ld b, a
|
|
||||||
ld c, #0x3D ; ESTEX GETMEM
|
|
||||||
rst #0x10
|
|
||||||
pop ix
|
|
||||||
jr c, _alloc_fail
|
|
||||||
ret ; CF=0 → A = blk_id, return as uint8 in A
|
|
||||||
_alloc_fail:
|
|
||||||
call __errno_set ; CF=1 → A = ESTEX errcode
|
|
||||||
xor a, a ; return 0 = failure sentinel
|
|
||||||
ret
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Release a block previously returned by mem_alloc_pages().
|
|
||||||
*
|
|
||||||
* in: blk_id (1..255).
|
|
||||||
* out: void; on ESTEX error errno is set (e.g. EINVAL for unknown id).
|
|
||||||
*
|
|
||||||
* Idempotent guarantees are NOT provided — freeing the same block twice
|
|
||||||
* sets errno on the second call. Caller is responsible for tracking
|
|
||||||
* ownership.
|
|
||||||
*/
|
|
||||||
void mem_free_block(uint8_t blk_id) __naked
|
|
||||||
{
|
|
||||||
(void)blk_id;
|
|
||||||
__asm
|
|
||||||
;; SDCC single-uint8 arg → A on entry.
|
|
||||||
push ix
|
|
||||||
ld c, #0x3E ; ESTEX FREEMEM
|
|
||||||
rst #0x10
|
|
||||||
pop ix
|
|
||||||
ret nc ; CF=0 → success
|
|
||||||
jp __errno_set ; CF=1 → A = ESTEX errcode; tail-call helper
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Translate a (block, page-index) pair into a physical 16-KB page number,
|
|
||||||
* suitable for OUT to PORT_PAGE_W1/W2/W3 or for bank_*() helpers.
|
|
||||||
*
|
|
||||||
* in: blk_id — id returned by mem_alloc_pages().
|
|
||||||
* idx — 0..(n-1), where n was the count passed to alloc.
|
|
||||||
* out: physical page number (1..255) on success;
|
|
||||||
* 0 on failure with errno set (invalid block or idx out of range).
|
|
||||||
*/
|
|
||||||
uint8_t mem_get_page(uint8_t blk_id, uint8_t idx) __naked
|
|
||||||
{
|
|
||||||
(void)blk_id; (void)idx;
|
|
||||||
__asm
|
|
||||||
;; 2-arg uint8/uint8: blk_id → A, idx → L.
|
|
||||||
push ix
|
|
||||||
ld b, l ; BIOS wants idx in B
|
|
||||||
;; A still has blk_id
|
|
||||||
ld c, #0xC4 ; BIOS EMM_GETPAGE
|
|
||||||
rst #0x08
|
|
||||||
pop ix
|
|
||||||
ret nc ; CF=0 → A = phys page (return value)
|
|
||||||
;; CF=1 → A = errcode; set errno, return 0 as sentinel.
|
|
||||||
call __errno_set
|
|
||||||
xor a, a
|
|
||||||
ret
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Query the EMM allocator about its current state.
|
|
||||||
*
|
|
||||||
* *total ← number of 16-KB physical pages installed in the system
|
|
||||||
* *free_pages ← number currently available for allocation
|
|
||||||
*
|
|
||||||
* Both pointers must be non-NULL writeable uint16_t locations.
|
|
||||||
* No error path: ESTEX INFOMEM always succeeds.
|
|
||||||
*/
|
|
||||||
void mem_info(uint16_t *total, uint16_t *free_pages) __naked
|
|
||||||
{
|
|
||||||
(void)total; (void)free_pages;
|
|
||||||
__asm
|
|
||||||
;; HL = total ptr, DE = free_pages ptr on entry.
|
|
||||||
;; RST 10 clobbers both — stash on the stack across the call.
|
|
||||||
push ix
|
|
||||||
push hl ; later [SP+2] = total_ptr
|
|
||||||
push de ; TOS [SP+0] = free_pages_ptr
|
|
||||||
|
|
||||||
ld c, #0x3C ; ESTEX INFOMEM → HL = total, BC = free
|
|
||||||
rst #0x10
|
|
||||||
|
|
||||||
pop de ; DE = free_pages_ptr
|
|
||||||
ld a, c
|
|
||||||
ld (de), a
|
|
||||||
inc de
|
|
||||||
ld a, b
|
|
||||||
ld (de), a ; *free_pages = BC
|
|
||||||
|
|
||||||
pop de ; DE = total_ptr
|
|
||||||
ld a, l
|
|
||||||
ld (de), a
|
|
||||||
inc de
|
|
||||||
ld a, h
|
|
||||||
ld (de), a ; *total = HL
|
|
||||||
|
|
||||||
pop ix
|
|
||||||
ret
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
@@ -15,3 +15,17 @@ void bank1_func(int x) __banked
|
|||||||
putchar('0' + x % 10);
|
putchar('0' + x % 10);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bank1_func2(int x) __banked
|
||||||
|
{
|
||||||
|
(void)x;
|
||||||
|
puts("BANK1-2: hello from a banked function (W1)!");
|
||||||
|
puts("BANK1-2: window 1 phys page = ");
|
||||||
|
hex8(_io_page_w1); /* should be BANK1's phys page */
|
||||||
|
putchar('\n');
|
||||||
|
putchar('1');
|
||||||
|
putchar('=');
|
||||||
|
putchar('0' + (x / 10) % 10);
|
||||||
|
putchar('0' + x % 10);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sprinter.h>
|
#include <sprinter.h>
|
||||||
|
|
||||||
|
void bank1_func2(int x) __banked;
|
||||||
|
|
||||||
void bank2_func(int x) __banked
|
void bank2_func(int x) __banked
|
||||||
{
|
{
|
||||||
(void)x;
|
(void)x;
|
||||||
@@ -15,4 +17,6 @@ void bank2_func(int x) __banked
|
|||||||
putchar('0' + (x / 10) % 10);
|
putchar('0' + (x / 10) % 10);
|
||||||
putchar('0' + x % 10);
|
putchar('0' + x % 10);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
|
bank1_func2(10);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -18,7 +18,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
errno = -1;
|
errno = -1;
|
||||||
int16_t a0 = 0, a1 = 0;
|
int16_t a0 = 0, a1 = 0;
|
||||||
a0 = get_text_attr();
|
a0 = get_text_attr();
|
||||||
|
|
||||||
uint8_t vMode = get_videotextmode();
|
uint8_t vMode = get_videotextmode();
|
||||||
set_videotextmode(TEXT_MODE_80x32);
|
set_videotextmode(TEXT_MODE_80x32);
|
||||||
@@ -52,7 +52,7 @@ int main(void)
|
|||||||
|
|
||||||
/* Back to a normal attribute so the goodbye reads cleanly. */
|
/* Back to a normal attribute so the goodbye reads cleanly. */
|
||||||
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
||||||
printf("a0=%d a1=%d now=%d errno=%d\n",
|
cprintf("a0=%d a1=%d now=%d errno=%d\n\r",
|
||||||
a0, a1, get_text_attr(), errno);
|
a0, a1, get_text_attr(), errno);
|
||||||
#ifdef DEBUG_RT
|
#ifdef DEBUG_RT
|
||||||
printf("w2_self_allocated = %u\n", w2_self_allocated);
|
printf("w2_self_allocated = %u\n", w2_self_allocated);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
* 5. Free the block, show the free-page count again.
|
* 5. Free the block, show the free-page count again.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
uint32_t buff[256];
|
||||||
|
|
||||||
static void show_mem(const char *label)
|
static void show_mem(const char *label)
|
||||||
{
|
{
|
||||||
uint16_t total, free_pages;
|
uint16_t total, free_pages;
|
||||||
@@ -28,6 +30,13 @@ int main(void)
|
|||||||
{
|
{
|
||||||
puts("Sprinter page allocator demo");
|
puts("Sprinter page allocator demo");
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
|
memset(buff, 0, sizeof(buff));
|
||||||
|
// printf("mem_io_ports_test() = 0x%02X\n", mem_io_ports_test());
|
||||||
|
printf("buff size (sizeof) = %u\n", sizeof(buff));
|
||||||
|
printf("buff size one element (sizeof) = %u\n", sizeof(buff[0]));
|
||||||
|
printf("buff first element = %u\n", buff[0]);
|
||||||
|
printf("buff last element = %u\n", buff[(sizeof(buff) / sizeof(buff[0])) - 1]);
|
||||||
|
|
||||||
show_mem("before:");
|
show_mem("before:");
|
||||||
|
|
||||||
|
|||||||
+15
-12
@@ -10,6 +10,8 @@
|
|||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <mouse.h>
|
#include <mouse.h>
|
||||||
|
|
||||||
|
mouse_state_t st;
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
||||||
@@ -29,7 +31,6 @@ int main(void)
|
|||||||
mouse_bounds_y(0, 255);
|
mouse_bounds_y(0, 255);
|
||||||
mouse_show();
|
mouse_show();
|
||||||
|
|
||||||
mouse_state_t st;
|
|
||||||
int last_x = -1, last_y = -1;
|
int last_x = -1, last_y = -1;
|
||||||
uint8_t last_btn = 0xFF;
|
uint8_t last_btn = 0xFF;
|
||||||
/* Sensitivity is a "raw steps per cursor pixel" divider: smaller =
|
/* Sensitivity is a "raw steps per cursor pixel" divider: smaller =
|
||||||
@@ -41,11 +42,13 @@ int main(void)
|
|||||||
mouse_set_sensitivity(sens_x, sens_y);
|
mouse_set_sensitivity(sens_x, sens_y);
|
||||||
int sens_dirty = 1;
|
int sens_dirty = 1;
|
||||||
|
|
||||||
|
st.x = st.y = 0; st.buttons = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
mouse_read(&st);
|
mouse_read(&st);
|
||||||
if (st.x != last_x || st.y != last_y || st.buttons != last_btn) {
|
if (st.x != last_x || st.y != last_y || st.buttons != last_btn) {
|
||||||
gotoxy(0, 6);
|
gotoxy(0, 6);
|
||||||
cprintf("x=%4u y=%4u text(%2u,%2u) buttons=0x%02X L%c R%c ",
|
printf("x=%4u y=%4u text(%2u,%2u) buttons=0x%02X L%c R%c ",
|
||||||
st.x, st.y,
|
st.x, st.y,
|
||||||
st.x / 8, st.y / 8,
|
st.x / 8, st.y / 8,
|
||||||
st.buttons,
|
st.buttons,
|
||||||
@@ -57,19 +60,19 @@ int main(void)
|
|||||||
}
|
}
|
||||||
if (sens_dirty) {
|
if (sens_dirty) {
|
||||||
gotoxy(0, 8);
|
gotoxy(0, 8);
|
||||||
cprintf("sensitivity horz=%3u vert=%3u ", sens_x, sens_y);
|
printf("sensitivity horz=%3u vert=%3u ", sens_x, sens_y);
|
||||||
sens_dirty = 0;
|
sens_dirty = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kbhit()) continue;
|
if (!kbhit()) continue;
|
||||||
int k = getch();
|
int k = getch();
|
||||||
if (k == 27) break; /* ESC */
|
if (k == 27) break; /* ESC */
|
||||||
if (k == '1' && sens_x > 1) { sens_x -= 1; sens_dirty = 1; }
|
if (k == '1' && sens_x > 1) { sens_x -= 1; sens_dirty = 1; }
|
||||||
if (k == '2' && sens_x < 254) { sens_x += 1; sens_dirty = 1; }
|
if (k == '2' && sens_x < 254) { sens_x += 1; sens_dirty = 1; }
|
||||||
if (k == '3' && sens_y > 1) { sens_y -= 1; sens_dirty = 1; }
|
if (k == '3' && sens_y > 1) { sens_y -= 1; sens_dirty = 1; }
|
||||||
if (k == '4' && sens_y < 254) { sens_y += 1; sens_dirty = 1; }
|
if (k == '4' && sens_y < 254) { sens_y += 1; sens_dirty = 1; }
|
||||||
if (sens_dirty)
|
if (sens_dirty)
|
||||||
mouse_set_sensitivity(sens_x, sens_y);
|
mouse_set_sensitivity(sens_x, sens_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
mouse_hide();
|
mouse_hide();
|
||||||
|
|||||||
Reference in New Issue
Block a user