Compare commits

...

17 Commits

Author SHA1 Message Date
snark13 bd116d7361 ChangeLog:
- update.
2026-06-10 10:41:03 +03:00
snark13 858e5755ad ChangeLog:
- big commit.
2026-06-10 10:35:48 +03:00
snark13 f87b52bb7f ChangeLog:
- memory optimized version.
2026-06-08 15:34:36 +03:00
snark13 07c4004bd9 ChangeLog:
- commit current version MDView.c
2026-06-08 11:22:15 +03:00
snark13 6992c1436e docs(mdview): добавить описание работы и тестовые паттерны
Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 22:39:14 +03:00
snark13 07c398a560 ChangeLog:
- Russian commentaries.
2026-06-07 22:01:13 +03:00
snark13 4bed9d3f3f fix(mdview): корректный multiline quote join в render_line
- исправлен rewind до первого non-cont сегмента для continuation
- для quote-потока newline обрабатывается как soft join с пропуском сырого ' > ' маркера
- восстановлен quote-префикс на continuation строках

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 20:26:19 +03:00
snark13 463a058f56 mdview: multiline quote paragraphs with quote-aware joining
- Quote blocks now join consecutive quote lines into one paragraph with
  soft breaks and wrap continuation under quote prefix.
- Empty quote lines ('>' / '>   ') act as quote-paragraph separators.
- Nested quote starts ('> > ...') are not merged into the parent text and
  stay separate rows.
- Keeps existing inline emphasis handling inside quote content.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 19:24:59 +03:00
snark13 8abc1d6c16 mdview: drop printf from status rendering
Replace status-line printf formatting with compact manual decimal rendering
(u16/u8 right-aligned helpers + wrchar/put_str_attr). This removes runtime
printf usage from mdview.c and keeps fixed status columns without stale digits.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 19:19:13 +03:00
snark13 982af12710 mdview: multiline list items with lazy continuation and blank-line grouping
- List items (UL/OL) can now span multiple source lines: non-marker
  lines are joined into the current item as lazy continuation.
- Continuation-line leading indentation is trimmed before joining so
  wrapped item text is separated by a single space.
- A single blank line between adjacent list markers is suppressed
  (same visual list), while 2+ blank lines still produce a separator.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 19:13:02 +03:00
snark13 394ee3a2cd mdview: blank-row block separation; tables nowrap; fix continuation marker
- Paragraph scanning no longer swallows the blank line separating it from
  the next block; runs of blank lines collapse to one row. Restores blank
  separation between paragraphs, headers and horizontal rules.
- Detect table rows (first non-space char '|') as nowrap segments; they are
  no longer merged into surrounding text or each other.
- Continuation (wrapped) rows render content as plain text and are no longer
  re-classified, so a wrapped word starting with '-'/'#'/'>' is not mis-drawn
  as a list/heading/quote marker.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 12:50:30 +03:00
snark13 47c9cd326a mdview: wide-break '\\', strikethrough, horizontal scroll bounds + '<'
- A trailing backslash before a newline now forces an in-paragraph line
  break (like two trailing spaces); render consumes the marker (non-code).
- Add ~~strikethrough~~ inline style (INIT_STYLE_STRIKE / EM_STRIKE),
  parsed in inline_scan, the paragraph merger and render, mirroring **.
- Horizontal pan is bounded by the widest nowrap segment on screen, and a
  '<' indicator marks hidden content off the left edge.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 11:55:19 +03:00
snark13 ca5f30b332 mdview: move segment index to EMM bank; inline hot byte reads
- Index is now an 8-byte record per visible segment in a dedicated EMM
  block (idx_get/idx_put), freeing ~11 KB of near RAM and lifting the
  old 2048-line cap (dynamic max_lines = index_pages * 2048).
- The per-byte scan keeps the previous segment offset in a near var
  (cur_seg_off) and mirrors the last record (cur_rec), so it never reads
  the index back from the bank.
- fb()/map_page() are inlined now that there is code headroom, removing
  per-byte call + 32-bit argument marshalling overhead.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 11:49:36 +03:00
snark13 0ad0559fc8 mdview: faster file-byte access (fb byte-decode + page cache)
Decode page/offset from the 32-bit offset's bytes to avoid SDCC z80
32-bit shift/mask helpers on the hot path; map_page() uses a cached
file_phys[] table to skip mem_get_page() on every W3 swap.
Behaviour-preserving.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-06-07 01:00:49 +03:00
snark13 035d93ab51 mdview: fix fenced code block emphasis leak and carry emphasis across wrap lines
- Add fenced code block tracking to the wrap-pass in index_lines().
  line_style is reset to PLAIN at every ``` delimiter, and all segs
  inside a fenced block get init_style=PLAIN. This prevents emphasis
  markers (e.g. _ in __var) inside code blocks from leaking into later
  normal text.
- Also carry init_style across wrap continuation segs so that a long
  bold/italic line that is wrapped continues with the correct style on
  the next segment.
- The fence bitmap pass now only updates in_code[], since init_style is
  already set correctly by the wrap pass.
2026-06-06 12:01:15 +03:00
snark13 0dedc4dac8 ChangeLog:
- prebuild.
2026-06-05 23:33:23 +03:00
snark13 737c974400 Add mdview markdown viewer, reorganize tests/examples and libc layout
- Split tests/ (libc feature tests) and examples/ (real apps); shared
  app.mk in repo root, was examples/example.mk
- libc/io/* split into libc/{conio,env,errno,file,mouse,string,sys,
  time,video}/ — clearer module boundaries
- New examples/mdview/: markdown viewer (Phases 1-5 + light nested
  lists). Headers (H1-H4), HR, ulist/olist/quote with nesting via
  leading spaces, fenced code blocks, inline emphasis (bold/italic/
  underscore/code), wrap/unwrap mode with soft wrap (F2), horizontal
  pan (← →) with '>' truncation indicator
- libc additions: scroll() in conio (ESTEX SCROLL), strlwr/strupr,
  gets() test
- Makefile updates across tests/ for the new shared app.mk path

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 22:23:36 +03:00
108 changed files with 2707 additions and 421 deletions
+18 -1
View File
@@ -9,7 +9,8 @@ build/
# sprinter-cc per-example intermediate directory
.sprinter-cc-*/
# Per-example final/intermediate outputs landing alongside the source
# Per-program final/intermediate outputs landing alongside the source
# (real apps under examples/ and libc feature tests under tests/).
examples/*/*.exe
examples/*/*.asm
examples/*/*.lst
@@ -23,6 +24,22 @@ examples/*/*.cdb
examples/*/*.mem
examples/*/*.rst
# Temporary build directory for floppy disk image preparation
examples/*/.disk_tmp/
tests/*/*.exe
tests/*/*.asm
tests/*/*.lst
tests/*/*.lk
tests/*/*.ihx
tests/*/*.noi
tests/*/*.sym
tests/*/*.map
tests/*/*.rel
tests/*/*.cdb
tests/*/*.mem
tests/*/*.rst
# libc archive (built from libc/, see lib/Makefile)
lib/*.lib
+32 -44
View File
@@ -1,58 +1,41 @@
# Sprinter C Compiler — top-level Makefile
#
# make build host tools, libc archive, and all examples
# make build host tools, libc archive, all tests, all apps
# make tools build only host tools (mkexe)
# make lib build lib/sprinter.lib (libc archive used by sprinter-cc)
# make examples build all examples
# make floppy package every example + test files into mame/v306/IMG/mc.img
# make tests build all libc feature tests under tests/
# make examples build all real applications under examples/
# make floppy package every .exe + test fixtures into mame/v306/IMG/mc.img
# make check run mkexe unit tests
# make clean remove all build artefacts
# make sdcc download/extract vendored SDCC
#
# Most heavy lifting is delegated to sub-Makefiles.
EXAMPLES := hello banked bankedbg strtest cat seek malloc mem_test argv errno rt_test openenv ls conio attrprob timedir mouse banklocl stdlib assrtest ptime stattest filetest gfx_demo gfx_d16 gfx_text gfx_mous
# Small libc-feature tests (one program per .c-language feature or libc API).
TESTS := hello banked bankedbg strtest cat seek malloc mem_test argv errno \
rt_test openenv ls conio attrprob timedir mouse banklocl stdlib \
assrtest ptime stattest filetest gfx_demo gfx_d16 gfx_text gfx_mous
# Larger end-user applications under examples/.
APPS := mdview
MAME_DIR := mame/v306
FLOPPY_IMG := $(MAME_DIR)/IMG/mc.img
MAKE_DISK := $(MAME_DIR)/make_disk.py
EXE_FILES := \
examples/hello/hello.exe \
examples/banked/banked.exe \
examples/bankedbg/bankedbg.exe \
examples/strtest/strtest.exe \
examples/cat/cat.exe \
examples/seek/seek.exe \
examples/malloc/malloc.exe \
examples/mem_test/mem_test.exe \
examples/argv/argv.exe \
examples/errno/errno.exe \
examples/rt_test/rt_test.exe \
examples/openenv/openenv.exe \
examples/ls/ls.exe \
examples/conio/conio.exe \
examples/attrprob/attrprob.exe \
examples/timedir/timedir.exe \
examples/mouse/mouse.exe \
examples/banklocl/banklocl.exe \
examples/stdlib/stdlib.exe \
examples/assrtest/assrtest.exe \
examples/ptime/ptime.exe \
examples/stattest/stattest.exe \
examples/filetest/filetest.exe \
examples/gfx_demo/gfx_demo.exe \
examples/gfx_d16/gfx_d16.exe \
examples/gfx_text/gfx_text.exe \
examples/gfx_mous/gfx_mous.exe
TEST_EXES := $(foreach t,$(TESTS),tests/$(t)/$(t).exe)
APP_EXES := $(foreach a,$(APPS),examples/$(a)/$(a).exe)
ALL_EXES := $(TEST_EXES) $(APP_EXES)
DATA_FILES := \
examples/cat/test.txt \
examples/seek/big.txt
tests/cat/test.txt \
tests/seek/big.txt \
examples/mdview/SAMPLE.MD
.PHONY: all tools lib examples check clean sdcc floppy $(EXAMPLES)
.PHONY: all tools lib tests examples check clean sdcc floppy $(TESTS) $(APPS)
all: tools lib examples
all: tools lib tests examples
tools:
$(MAKE) -C toolchain/mkexe
@@ -63,18 +46,22 @@ lib:
check: tools
$(MAKE) -C toolchain/mkexe check
examples: $(EXAMPLES)
tests: $(TESTS)
examples: $(APPS)
$(EXAMPLES): tools lib
$(TESTS): tools lib
$(MAKE) -C tests/$@
$(APPS): tools lib
$(MAKE) -C examples/$@
# Generate big.txt if missing (gen_bigfile.py creates 100 KB marker file).
examples/seek/big.txt:
cd examples/seek && python3 gen_bigfile.py big.txt 102400
tests/seek/big.txt:
cd tests/seek && python3 gen_bigfile.py big.txt 102400
# Re-pack the MAME floppy image with every built example + needed data files.
floppy: examples examples/seek/big.txt
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXE_FILES) $(DATA_FILES)
# Re-pack the MAME floppy image with every built exe + needed data files.
floppy: tests examples tests/seek/big.txt
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(ALL_EXES) $(DATA_FILES)
@echo
@echo "Floppy ready: $(FLOPPY_IMG)"
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
@@ -82,7 +69,8 @@ floppy: examples examples/seek/big.txt
clean:
$(MAKE) -C toolchain/mkexe clean
$(MAKE) -C lib clean
@for e in $(EXAMPLES); do $(MAKE) -C examples/$$e clean; done
@for t in $(TESTS); do $(MAKE) -C tests/$$t clean; done
@for a in $(APPS); do $(MAKE) -C examples/$$a clean; done
sdcc:
bash third_party/setup-sdcc.sh
+1 -2
View File
@@ -52,7 +52,7 @@ pages by program size — small programs get only one page. Pick a memory mode
on what your program needs:
| Mode | Code lives in | Banking | Use when | Note |
|---|---|---|---|
|---|---|---|---|---|
| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | |
| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | |
| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | |
@@ -217,7 +217,6 @@ This repository contains:
* **Sprinter / Peters Plus** — Иван Мак, Дмитрий Паринов and the original team
* **SDCC** — for the underlying Z80 compiler
* **z88dk +pps** — Дмитрий M. for paving the way with the first Sprinter target
* **MAME** — for the Sprinter Sp2000 emulation
---
+8 -6
View File
@@ -1,9 +1,11 @@
# example.mk — shared Makefile fragment for simple Sprinter ESTEX examples.
# app.mk — shared Makefile fragment for any standalone Sprinter ESTEX
# program — used both by libc feature tests under tests/ and by real
# applications under examples/.
#
# Usage in an example's Makefile:
# Usage in a per-program Makefile:
#
# PROJ_ROOT := $(abspath $(CURDIR)/../..)
# EXAMPLE := my_example # base name (matches my_example.c)
# EXAMPLE := my_program # base name (matches my_program.c)
#
# # Optional overrides (any combination):
# # MEMORY := small # tiny | small | big | huge | manual
@@ -12,7 +14,7 @@
# # EXTRA_FLAGS := --crt0=minimal # passed through to sprinter-cc
# # EXTRA_DATA := test.txt # extra files to add to `make floppy`
#
# include $(PROJ_ROOT)/examples/example.mk
# include $(PROJ_ROOT)/app.mk
#
# Pipeline (all driven by sprinter-cc):
# crt0 + EXAMPLE.c + EXTRA_SRCS --sdcc--> .ihx
@@ -61,9 +63,9 @@ $(LIB):
clean:
rm -rf .sprinter-cc-* $(EXAMPLE).exe
# `make floppy` packs ONLY this example (+ optional EXTRA_DATA files) into
# `make floppy` packs ONLY this program (+ optional EXTRA_DATA files) into
# the MAME floppy image, replacing whatever was there. Handy for trying a
# single program without rebuilding all 27 examples.
# single program without rebuilding everything.
floppy: $(EXAMPLE).exe
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(EXTRA_DATA)
@echo
+188
View File
@@ -0,0 +1,188 @@
# MDView: описание работы и тестовые паттерны
## Назначение
`mdview` — консольный просмотрщик Markdown для текстового режима 80x32.
Программа загружает `.MD`-файл, индексирует его на экранные сегменты и рендерит содержимое с учётом переносов, стилей и специальных блоков.
## Как работает программа
### 1) Загрузка файла
- На старте вызывается `load_file(path)`.
- Если путь не передан, используется `SAMPLE.MD`.
- При ошибках загрузки программа показывает причину и завершает работу после нажатия клавиши.
### 2) Индексация (`index_lines`)
- Файл разбирается в массив сегментов для быстрого рендера.
- Поддерживаются ключевые типы строк:
- заголовки `#..####`,
- маркированные/нумерованные списки,
- цитаты `>`,
- горизонтальные разделители,
- fenced code-блоки,
- таблицы (nowrap-режим),
- обычные параграфы.
- Для list/quote/plain используется общая логика сканирования с режимами склейки строк.
### 3) Рендер (`render_line`, `render_viewport`)
- Для каждой видимой строки рисуется префикс (маркер списка, цитаты и т.д.) и текст.
- Поддерживаются inline-стили:
- `**жирный**`,
- `*курсив*`,
- `_подчёркнутый_`,
- `~~зачёркнутый~~`,
- `` `code` ``.
- Табы расширяются до фиксированного шага.
- Для длинных nowrap-строк применяется горизонтальный сдвиг и индикаторы `<`/`>`.
### 4) Навигация
- `Up/Down` — прокрутка на 1 строку.
- `PgUp/PgDn` — прокрутка на экран.
- `Home/End` — начало/конец документа.
- `Left/Right` — горизонтальная прокрутка nowrap-контента.
- `Esc` или `F10` — выход.
## Сборка и запуск
```bash
make -C examples/mdview
```
Запуск на целевой системе:
```bash
mdview.exe <путь_к_файлу.md>
```
Если путь не указан, открывается `SAMPLE.MD`.
## Тестовые паттерны
Ниже набор паттернов для ручной регрессии.
### P01 — Базовый smoke-тест открытия
Вход:
- валидный markdown-файл среднего размера.
Ожидается:
- файл открывается без ошибок;
- статус-бар и меню отображаются корректно;
- прокрутка работает.
### P02 — Ошибка открытия файла
Вход:
- несуществующий путь к файлу.
Ожидается:
- сообщение `mdview: cannot load file`;
- корректный код причины (`open failed` и т.п.);
- ожидание клавиши перед выходом.
### P03 — Пустой файл
Вход:
- пустой `.md`.
Ожидается:
- сообщение `mdview: empty file`;
- корректное завершение после нажатия клавиши.
### P04 — Обычный параграф и переносы
Шаблон:
```md
Это длинный абзац для проверки переноса строк в обычном тексте.
Вторая строка должна мягко склеиться с первой.
```
Ожидается:
- строки склеиваются как единый параграф;
- переносы происходят по ширине экрана без потери символов.
### P05 — Hard break в параграфе
Шаблон:
```md
Первая строка с двумя пробелами в конце.
Вторая строка после hard break.
```
Ожидается:
- между строками сохраняется принудительный разрыв;
- inline-стиль не ломается.
### P06 — Многострочный список с lazy continuation
Шаблон:
```md
- Пункт списка, который продолжается
на следующей строке с отступом.
```
Ожидается:
- continuation-строка склеивается с пунктом через один пробел;
- лишние ведущие отступы continuation не попадают в итоговый текст;
- переносы не ломают маркер списка.
### P07 — Многострочная цитата
Шаблон:
```md
> Первая строка цитаты
> Вторая строка цитаты
> Третья строка цитаты
```
Ожидается:
- для продолжений корректно повторяется quote-префикс;
- сырой символ `>` из исходника не «просачивается» в середину текста.
### P08 — Пустая quoted-строка как разделитель
Шаблон:
```md
> Первый параграф цитаты
>
> Второй параграф цитаты
```
Ожидается:
- пустая quoted-строка разделяет два параграфа;
- рендер не объединяет их в один поток.
### P09 — Таблица в nowrap-режиме
Шаблон:
```md
| col1 | col2 | col3 |
|------|------|------|
| очень длинное значение | очень длинное значение | очень длинное значение |
```
Ожидается:
- строка таблицы не переносится по словам;
- работает горизонтальный сдвиг `Left/Right`;
- индикаторы `<`/`>` показывают скрытый контент.
### P10 — Fenced code-block
Шаблон:
~~~md
```text
**это не жирный**
_это не подчёркнутый_
```
~~~
Ожидается:
- внутри code-блока inline-разметка не применяется;
- текст отображается как литерал.
### P11 — Inline-стили на обычном тексте
Шаблон:
```md
Текст с **жирным**, *курсивом*, _подчёркнутым_, ~~зачёркнутым~~ и `code`.
```
Ожидается:
- все перечисленные стили рендерятся корректно;
- литеральные случаи вроде `2 * 3` не превращаются в стиль.
### P12 — Статус-бар и границы прокрутки
Вход:
- документ на несколько экранов.
Ожидается:
- корректные `L x-y / total` и `%`;
- `Home/End` приводят к ожидаемым позициям;
- при выходе за границы документа top-line корректно clamp-ится.
## Рекомендация по регрессии
После любых изменений в логике индексации/рендера прогонять минимум:
- `P04`, `P05`, `P06`, `P07`, `P09`, `P10`, `P11`, `P12`.
+39
View File
@@ -0,0 +1,39 @@
# Build mdview.exe — Markdown viewer for Sprinter.
#
# small memory mode: code in W1, data/stack/heap in W2 (32 KB total).
# W3 stays free for the file buffer (EMM-mapped).
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := mdview
MEMORY := small
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
+133
View File
@@ -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 КБ, адреса 0x40000xBFFF).
- Буфер файла — страницы EMM, отображаемые в W3 (0xC0000xFFFF)
## 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 Петров А.Г.*
File diff suppressed because it is too large Load Diff
+39 -16
View File
@@ -22,25 +22,48 @@ BUILD := $(PROJ_ROOT)/lib/build
# All libc C modules.
LIBC_C := \
libc/io/atexit.c libc/io/conio.c libc/io/cprintf.c libc/io/dir.c \
libc/io/videomode_raw.c \
libc/io/_errno_set.c \
libc/io/env.c libc/io/errno.c libc/io/fsdir.c \
libc/io/lseek.c libc/io/mouse.c libc/io/open.c \
libc/io/read.c libc/io/sleep.c \
libc/io/time.c libc/io/posix_time.c libc/io/unlink.c \
libc/sys/atexit.c \
libc/conio/conio.c \
libc/conio/cprintf.c \
libc/conio/text_palette.c \
libc/io/dir.c \
libc/video/videomode_raw.c \
libc/video/palette.c \
libc/errno/_errno_set.c \
libc/env/env.c \
libc/errno/errno.c \
libc/io/fsdir.c \
libc/io/lseek.c \
libc/mouse/mouse.c \
libc/io/open.c \
libc/io/read.c \
libc/time/sleep.c \
libc/time/time.c \
libc/time/posix_time.c \
libc/io/unlink.c \
libc/io/stat.c \
libc/mem/bank_io_w3.c libc/mem/bank_io_w1.c libc/mem/mem_alloc.c \
libc/gfx/gfx_core.c libc/gfx/gfx_raw_common.c \
libc/gfx/gfx_raw_256.c libc/gfx/gfx_raw_16.c \
libc/gfx/gfx_256.c libc/gfx/gfx_16.c \
libc/gfx/gfx_font.c libc/gfx/gfx_text_256.c \
libc/mem/bank_io_w3.c \
libc/mem/bank_io_w1.c \
libc/mem/mem_estex.c \
libc/mem/mem_bios.c \
libc/gfx/gfx_core.c \
libc/gfx/gfx_palette.c \
libc/gfx/gfx_raw_common.c \
libc/gfx/gfx_raw_256.c \
libc/gfx/gfx_raw_16.c \
libc/gfx/gfx_256.c \
libc/gfx/gfx_16.c \
libc/gfx/gfx_font.c \
libc/gfx/gfx_text_256.c \
libc/gfx/gfx_text_16.c \
libc/stdio/getchar.c \
libc/stdio/putchar.c libc/stdio/puts.c libc/stdio/file.c \
libc/stdio/hex_print.c libc/stdio/dec_print.c \
libc/stdio/solid_helpers.c \
libc/io/solid_compat.c
libc/stdio/putchar.c \
libc/stdio/puts.c \
libc/file/file.c \
libc/stdio/hex_print.c \
libc/stdio/dec_print.c \
libc/string/strlwr.c \
libc/string/strupr.c
# Runtime modules to bundle (pulled by symbol references from libc-using code).
# NOTE: runtime/bank.s is NOT bundled — its trampoline depends on the banking
+62
View File
@@ -58,6 +58,30 @@ char getche(void) __naked
__endasm;
}
/* getkey — like getch() but exposes BOTH the ASCII value and the
* positional scan code, so callers can distinguish extended keys
* (arrows, F1..F12, PgUp/PgDn, Home/End, Ins/Del all of which carry
* ASCII == 0 from ESTEX) from plain ASCII keys.
*
* return = (scan << 8) | ascii
*
* For plain keys: ascii holds the character, scan holds the positional
* code (bit 7 set when Ctrl/Alt/Shift is held).
* For extended keys: ascii == 0, scan identifies the key (see KEY_* in
* <conio.h>).
*/
uint16_t getkey(void) __naked
{
__asm
push ix
ld c, #0x30 ; ESTEX WAITKEY: A=ASCII, D=scan, E=ASCII
rst #0x10
pop ix
ld e, a ; E = ASCII (defensive: A is the canonical copy)
ret ; __sdcccall(1) returns uint16_t in DE
__endasm;
}
/* ---- putch / cputs: Turbo-C conio convention ---------------------- *
* Both APPLY the current text attribute (g_text_attr). When attr is
* KEEP_EXIST_ATTR (>0xFF), they short-circuit to the FAST stdio path
@@ -332,6 +356,11 @@ char cputs(const char *s) __naked
or a, l
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
ld a, (_g_text_attr + 1)
or a, a
@@ -374,11 +403,13 @@ char cputs(const char *s) __naked
_cputs_loop_end:
call __set_cursor
pop ix ; restore caller's IX
xor a, a ; return 0
ret
_cputs_fast:
call __cputs_pchars
pop ix ; restore caller's IX
xor a, a ; return 0
ret
__endasm;
@@ -462,6 +493,37 @@ uint16_t wherexy(void) __naked
__endasm;
}
/* wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr)
*
* SDCC __sdcccall(1): x in A, y in L (2 uint8 A, L); ch and attr
* packed and pushed on the stack as a single 16-bit value (caller does
* `ld hl, #(attr<<8)|ch; push hl`). Layout after CALL:
* [SP+0..1] = return address
* [SP+2] = ch (low half of pushed pair)
* [SP+3] = attr (high half)
* Void return callee-pops the 2 stack-arg bytes via `pop bc` + jp (iy).
*/
void scroll(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t direction, uint8_t clear) __naked
{
(void)x; (void)y; (void)w; (void)h; (void)direction; (void)clear;
__asm
pop iy ; return address
ld d, l ; D = row (y)
ld e, a ; E = col (x)
pop hl ; H = heigth(h), L = width(w)
pop bc ; C = direction, B = clear
ld a, b ; A = clear(B)
ld b, c ; B = direction(C)
push ix
ld c, #0x55 ; ESTEX SCROLL
rst #0x10
pop ix
jp (iy)
__endasm;
}
/* wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr)
*
View File
+4 -3
View File
@@ -143,7 +143,8 @@ int fputc(int c, FILE *fp)
{
if (!fp) { errno = EBADF; return EOF; }
if (fp->flags & _F_CONOUT) {
return putchar(c);
putchar(c);
return (int)c;
}
if (!(fp->flags & _F_WRITE)) { errno = EBADF; return EOF; }
uint8_t ch = (uint8_t)c;
@@ -175,7 +176,7 @@ int fputs(const char *s, FILE *fp)
if (!fp || !s) { errno = EBADF; return EOF; }
if (fp->flags & _F_CONOUT) {
while (*s) {
if (putchar((unsigned char)*s++) == EOF) return EOF;
putchar((unsigned char)*s++);;
}
return 0;
}
@@ -239,7 +240,7 @@ size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp)
const char *p = (const char *)ptr;
size_t total = size * nmemb;
for (size_t i = 0; i < total; i++) {
if (putchar((unsigned char)p[i]) == EOF) return i / size;
putchar((unsigned char)p[i]);
}
return nmemb;
}
+4 -41
View File
@@ -13,7 +13,7 @@
* 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_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):
* _gfx_addr_base — 0xC000 for page 0, 0xC140 for page 1. Every
@@ -136,43 +136,6 @@ void gfx_wait_vsync(void) __naked
__endasm;
}
/* ---- Palette (BIOS $A4 PIC_SET_PAL) ----------------------------- */
static uint8_t pal_num_;
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);
}
/* 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
* libc/video/palette.c for the shared low-level $A4 / $A6 implementation. */
+86 -4
View File
@@ -38,6 +38,42 @@
char kbhit (void);
char getch (void);
char getche(void);
/* Extended-key reader. Returns (scan << 8) | ascii. Plain ASCII keys
* have ascii in the low byte; extended keys (arrows / F1..F12 /
* PgUp/PgDn / Home / End / Ins / Del) carry ascii == 0 and the
* KEY_* code in the high byte. */
uint16_t getkey(void);
/* Scan codes returned in the high byte of getkey() when the low byte
* (ASCII) is 0. Empirically verified in MAME — the ProgrammerManual.txt
* "positional code" column is misleading; BIOS returns IBM-style codes
* for the F-keys and a "5N + numpad-position" pattern for the cursor /
* editing keys.
*
* Verified 2026-06-04 by reading raw getkey() output. */
#define KEY_F1 0x3B
#define KEY_F2 0x3C
#define KEY_F3 0x3D
#define KEY_F4 0x3E
#define KEY_F5 0x3F
#define KEY_F6 0x40
#define KEY_F7 0x41
#define KEY_F8 0x42
#define KEY_F9 0x43
#define KEY_F10 0x44
#define KEY_F11 0x45 /* not verified */
#define KEY_F12 0x46 /* not verified */
#define KEY_END 0x51
#define KEY_DOWN 0x52
#define KEY_PGDN 0x53
#define KEY_LEFT 0x54
#define KEY_RIGHT 0x56
#define KEY_HOME 0x57
#define KEY_UP 0x58
#define KEY_PGUP 0x59
#define KEY_INS 0x50 /* numpad 0; not verified */
#define KEY_DEL 0x55 /* numpad 5/.; not verified */
char putch (char c);
char cputs (const char *s);
int cprintf(const char *fmt, ...);
@@ -63,6 +99,8 @@ uint8_t wherex (void);
uint8_t wherey (void);
uint16_t wherexy(void); // high byte = Y, low byte = X coords.
void scroll(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
/* Direct character/attribute screen access (ESTEX $57 / $58).
* wrchar — write char + attribute at (x, y); does NOT advance the cursor
* and does NOT interpret control characters. Useful for
@@ -143,12 +181,56 @@ uint8_t get_putch_raw_mode(void);
* Colour order is standard CGA / Borland-conio.h. Constants 0..7 are
* usable for both fg and bg; 8..15 are foreground-only. */
enum {
COLOR_BLACK = 0, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_LIGHTGRAY,
COLOR_DARKGRAY, COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN,
COLOR_LIGHTRED, COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE
COLOR_BLACK = 0,
COLOR_BLUE,
COLOR_GREEN,
COLOR_CYAN,
COLOR_RED,
COLOR_MAGENTA,
COLOR_BROWN,
COLOR_LIGHTGRAY,
COLOR_DARKGRAY,
COLOR_LIGHTBLUE,
COLOR_LIGHTGREEN,
COLOR_LIGHTCYAN,
COLOR_LIGHTRED,
COLOR_LIGHTMAGENTA,
COLOR_YELLOW,
COLOR_WHITE
};
#define COLOR_BLINK 0x80u
#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
+15 -7
View File
@@ -16,14 +16,22 @@
#ifndef FCNTL_H
#define FCNTL_H
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#ifndef _STD_SEEK_
#define _STD_SEEK_
/* constants to be used as 3rd argument for "fseek" function */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
#define O_CREAT 0x040
#define O_EXCL 0x080
#define O_TRUNC 0x200
#define O_APPEND 0x400
/* Definition "open flags" */
#define O_WRONLY 0x01 /* 0 file write only */
#define O_RDONLY 0x02 /* 1 file read only */
#define O_RDWR 0x03 /* 1,0 file read/write */
#define O_TRUNC 0x04 /* 2 open with truncation */
#define O_CREAT 0x08 /* 3 create and open file */
#define O_EXCL 0x10 /* 4 exclusive open */
#define O_APPEND 0x20 /* 5 to end of file */
int open (const char *path, int flags);
int creat(const char *path, int mode); /* mode arg ignored on Sprinter */
+11
View File
@@ -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,
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
+28 -4
View File
@@ -47,23 +47,47 @@
* n : 1..255
* 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(). */
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().
* On error errno is set (e.g. EINVAL for unknown id). Double-free is
* 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
* for sprinter_page_w1/w2/w3() or the bank_*() helpers below.
* blk_id: from mem_alloc_pages()
* idx : 0..(n-1)
* 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.
* 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)
+1 -1
View File
@@ -30,7 +30,7 @@ int vprintf(const char *, va_list);
int vsprintf(char *, const char *, va_list);
/* puts / putchar / getchar — overridden by our libc to use ESTEX. */
int puts (const char *);
char puts (const char *);
int putchar(int);
int getchar(void);
-29
View File
@@ -1,29 +0,0 @@
/*
* solid_compat.c — Solid-C compatibility helpers that need real code
* (rather than just header macros).
*/
#include <sprinter_compat.h>
#include <ctype.h>
char *strlwr(char *s)
{
char *p = s;
while (*p) {
if (*p >= 'A' && *p <= 'Z') *p += 'a' - 'A';
p++;
}
return s;
}
char *strupr(char *s)
{
char *p = s;
while (*p) {
if (*p >= 'a' && *p <= 'z') *p -= 'a' - 'A';
p++;
}
return s;
}
/* div() comes from SDCC's z80.lib. */
-137
View File
@@ -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;
}
-6
View File
@@ -17,13 +17,7 @@ int getchar(void) __naked
ld c, #0x30 ; ESTEX WAITKEY
rst #0x10
pop ix
ld a, e ; E = ASCII (already the low byte of our return DE)
or a, a
jr Z, no_ascii
ld d, #0
ret
no_ascii:
ld de, #-1
ret
__endasm;
}
+6 -14
View File
@@ -19,23 +19,15 @@ int putchar(int c) __naked
{
(void)c;
__asm
ld a, l ; SDCC __sdcccall(1) int HL
push ix
ld a, l
cp #0x0A
jr nz, _pc_emit
ld a, #0x0D ; CR before LF
push af
jr nz, cputc
call cputc
ld a, #0x0D
cputc:
ld c, #0x5B
rst #0x10
pop af
ld a, #0x0A
_pc_emit:
push af
ld c, #0x5B
rst #0x10
pop af
pop ix
ld e, a
ld e, l
ld d, #0
ret
__endasm;
+21 -34
View File
@@ -14,17 +14,11 @@
* - Avoid trailing PUTCHAR after PCHARS — empirically that sometimes
* drops the next char. Embed the line ending inside the PCHARS
* buffer instead.
* - Strings longer than the buffer fall back to per-char putchar so
* we never silently truncate.
*/
#include <stdio.h>
#include <stdint.h>
#define PUTS_BUF_SIZE 256 /* body bytes before CR expansion */
static char puts_buf[PUTS_BUF_SIZE + 3]; /* +3 for trailing CR LF NUL */
static void pchars(const char *s) __naked
{
(void)s;
@@ -37,33 +31,26 @@ static void pchars(const char *s) __naked
__endasm;
}
int puts(const char *s)
char puts(const char *s) __naked
{
uint16_t n = 0;
uint16_t i = 0;
while (s[i] && n < PUTS_BUF_SIZE - 1) {
char c = s[i++];
if (c == '\n') {
puts_buf[n++] = '\r';
puts_buf[n++] = '\n';
} else {
puts_buf[n++] = c;
}
}
if (s[i]) {
/* Overflow — char-by-char fallback so we never truncate. */
for (uint16_t k = 0; s[k]; k++)
putchar((unsigned char)s[k]);
putchar('\n');
return 0;
}
puts_buf[n++] = '\r';
puts_buf[n++] = '\n';
puts_buf[n] = 0;
pchars(puts_buf);
return 0;
(void)s;
__asm
puts_:
ld a, (hl)
or a
jr z, fin_
push hl
ld l, a
ld h, #0
call _putchar
pop hl
inc hl
jp puts_
;
fin_:
ld l, #0x0A
ld h, #0
call _putchar
ret
__endasm;
}
-27
View File
@@ -1,27 +0,0 @@
/*
* solid_helpers.c — small Solid-C compatibility helpers.
*
* dec8 / dec16 / dec32 / hex8 / hex16 / hex32 are in dec_hex.c (compact
* asm port from solid-c's STDLIB.ASM, ~150 bytes total — vs ~3-5 KB if
* routed through printf). This file now only holds gets().
*/
#include <stdio.h>
/* ---- gets — dangerous but Solid-C provides it ---------------------- */
char *gets(char *buf)
{
int i = 0;
int c;
for (;;) {
c = getchar();
if (c == EOF) {
if (i == 0) return 0;
break;
}
if (c == '\n' || c == '\r') break;
buf[i++] = (char)c;
}
buf[i] = 0;
return buf;
}
+23
View File
@@ -0,0 +1,23 @@
/*
* solid_compat.c — Solid-C compatibility helpers that need real code
* (rather than just header macros).
*
* CP866 Cyrillic support: strlwr/strupr handle uppercase/lowercase
* conversion for both Latin and Cyrillic characters in CP866 code page
* (bytes 0x800xFF).
*/
#include <sprinter_compat.h>
#include <ctype.h>
char *strlwr(char *s)
{
char *p = s;
while (*p) {
if ((*p >= 'A' && *p <= 'Z') || (*p >= 0x80 && *p <= 0x8F)) *p += 'a' - 'A';
else if ((*p >= 0x90 && *p <= 0x9F)) *p += 0x50;
else if ((*p == 0xF0)) *p = 0xF1;
p++;
}
return s;
}
+23
View File
@@ -0,0 +1,23 @@
/*
* solid_compat.c — Solid-C compatibility helpers that need real code
* (rather than just header macros).
*
* CP866 Cyrillic support: strlwr/strupr handle uppercase/lowercase
* conversion for both Latin and Cyrillic characters in CP866 code page
* (bytes 0x800xFF).
*/
#include <sprinter_compat.h>
#include <ctype.h>
char *strupr(char *s)
{
char *p = s;
while (*p) {
if ((*p >= 'a' && *p <= 'z') || (*p >= 0xA0 && *p <= 0xAF)) *p -= 'a' - 'A';
else if ((*p >= 0xE0 && *p <= 0xEF)) *p -= 0x50;
else if ((*p == 0xF1)) *p = 0xF0;
p++;
}
return s;
}
View File
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := argv
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := assrtest
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := attrprob
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -8,4 +8,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := banked
MEMORY := huge
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -9,4 +9,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := bankedbg
MEMORY := big
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -15,3 +15,17 @@ void bank1_func(int x) __banked
putchar('0' + x % 10);
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 <sprinter.h>
void bank1_func2(int x) __banked;
void bank2_func(int x) __banked
{
(void)x;
@@ -15,4 +17,6 @@ void bank2_func(int x) __banked
putchar('0' + (x / 10) % 10);
putchar('0' + x % 10);
putchar('\n');
bank1_func2(10);
}
@@ -9,4 +9,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := banklocl
MEMORY := huge
EXTRA_FLAGS := --bank 1=bank1.c --mkexe -p --mkexe 0
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
+1 -1
View File
@@ -3,4 +3,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := cat
EXTRA_DATA := test.txt
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := conio
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := conio2
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := dec_test
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := errno
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := filetest
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
+5
View File
@@ -0,0 +1,5 @@
# Build cat.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gets
include $(PROJ_ROOT)/app.mk
+19
View File
@@ -0,0 +1,19 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
char buff [256];
puts("--- gets test ---");
gets(buff);
puts("");
puts("loaded string:");
puts(buff);
puts("done");
(void)getchar();
return 0;
}
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_d16
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_dbuf
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_demo
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_mous
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_text
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := hello
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -18,7 +18,7 @@ int main(void)
{
errno = -1;
int16_t a0 = 0, a1 = 0;
a0 = get_text_attr();
a0 = get_text_attr();
uint8_t vMode = get_videotextmode();
set_videotextmode(TEXT_MODE_80x32);
@@ -52,7 +52,7 @@ int main(void)
/* Back to a normal attribute so the goodbye reads cleanly. */
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);
#ifdef DEBUG_RT
printf("w2_self_allocated = %u\n", w2_self_allocated);
+1 -1
View File
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := ls
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
View File
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := malloc
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := mem_test
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -16,6 +16,8 @@
* 5. Free the block, show the free-page count again.
*/
uint32_t buff[256];
static void show_mem(const char *label)
{
uint16_t total, free_pages;
@@ -28,6 +30,13 @@ int main(void)
{
puts("Sprinter page allocator demo");
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:");
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := mouse
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
+15 -12
View File
@@ -10,6 +10,8 @@
#include <conio.h>
#include <mouse.h>
mouse_state_t st;
int main(void)
{
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
@@ -29,7 +31,6 @@ int main(void)
mouse_bounds_y(0, 255);
mouse_show();
mouse_state_t st;
int last_x = -1, last_y = -1;
uint8_t last_btn = 0xFF;
/* Sensitivity is a "raw steps per cursor pixel" divider: smaller =
@@ -41,11 +42,13 @@ int main(void)
mouse_set_sensitivity(sens_x, sens_y);
int sens_dirty = 1;
st.x = st.y = 0; st.buttons = 0;
while (1) {
mouse_read(&st);
if (st.x != last_x || st.y != last_y || st.buttons != last_btn) {
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 / 8, st.y / 8,
st.buttons,
@@ -57,19 +60,19 @@ int main(void)
}
if (sens_dirty) {
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;
}
if (!kbhit()) continue;
int k = getch();
if (k == 27) break; /* ESC */
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 == '3' && sens_y > 1) { sens_y -= 1; sens_dirty = 1; }
if (k == '4' && sens_y < 254) { sens_y += 1; sens_dirty = 1; }
if (sens_dirty)
mouse_set_sensitivity(sens_x, sens_y);
if (!kbhit()) continue;
int k = getch();
if (k == 27) break; /* ESC */
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 == '3' && sens_y > 1) { sens_y -= 1; sens_dirty = 1; }
if (k == '4' && sens_y < 254) { sens_y += 1; sens_dirty = 1; }
if (sens_dirty)
mouse_set_sensitivity(sens_x, sens_y);
}
mouse_hide();
@@ -4,4 +4,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := openenv
MEMORY := big
EXTRA_FLAGS :=
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := ptime
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -2,4 +2,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := rt_test
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -3,4 +3,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := seek
EXTRA_DATA := big.txt
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk
@@ -1,3 +1,3 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := stattest
include $(PROJ_ROOT)/examples/example.mk
include $(PROJ_ROOT)/app.mk

Some files were not shown because too many files have changed in this diff Show More