если разрешить подгружать свой шедулер, то можно сделать такой, который будет сохранять лог переключений и контроля ресурсов для показа графика

A process consists of an address space, object handles, and one or more paths of execution (threads). Threads are used to perform work. For example, a thread can respond to interrupts and handle asynchronous process-related events in thread context. 

RTX timers are not synchronization objects which means threads cannot wait on an RTX timer handle. This is in contrast to the Windows waitable timer, which is an object on which a thread can wait, or against which a thread can receive notification.

event - объект синхронизации. делает pulse (его ждут через waitforsingleobject)


допустим, что система сначала будет поставляться в виде компиляции ядра с лоадером, сценария работы и набора программ
(другой вариант - в виде снапшота)
лоадер должен быть и в компиляции "недоланг", т.к. надо запускать сгенерированные программы
а из сгенерированных программ должен быть выход, при этом редактор и тексты не должны перезагружаться
но перед запуском надо рекомендовать сохранение
лоадер может выполнять все функции линкера, т.е. грузить прямо объектники из ассемблера (сейчас он состоит из двух файлов - так удобнее, а в будущем для всей программы может быть много пар файлов, но при include не нужно)

для бинарников, экспортируемых в другие системы, нужен линкер

лоадеру(линкеру) нужна следующая информация (будем пока считать, что ассемблер для сборки вызывался один раз):
1. список всех блоков в объектнике: их типы, алигны и размеры.
типы:
- fixed (для оси не надо? с адресом)
- nearcode
- [initialized neardata (==nearcode?)]
- [empty neardata]
- farcode
- [initialized fardata (==farcode)])
- [empty fardata]
2. для каждого блока список точек релокации - добавить третий файл или вписать в поток данных с нарушением размеров команд?
3. поток данных
4. размер стека
5. откуда запускать

точки релокации могут быть следующих типов:
1. параметр 16-битной команды или данное (какой номер блока, какое смещение)
[2. HSB параметр 8-битной команды (какой номер блока, какое смещение)]
[3. LSB параметр 8-битной команды (какой номер блока, какое смещение)]
4. страница для far вызова (какой номер блока)
5. напоминалка FIXUP "пропатчить текущим адресом такой-то номер блока, такое-то смещение"
5. напоминалка FIXUP "пропатчить текущей страницей такой-то номер блока, такое-то смещение"

как можно связать каждый блок с каждым?
как ассемблер будет знать смещение в другом блоке?
уже понятно, что метки должны иметь поле "номер блока" (сейчас соответствует странице, но не должен, и должен быть 2-байтным)
при двух проходах ассемблирования все смещения в любом другом блоке будут известны
(можно в один проход:
- если не нашли метку, то пишем для неё пост FIXUP "когда появишься, сгенерируй напоминалку FIXUP пропатчить текущим адресом такой-то номер блока, такое-то смещение" (для одной метки может быть много постов!!!), а вычисления запретить;
- если не нашли страницу метки, то пишем для неё пост FIXUP "когда появишься, сгенерируй напоминалку FIXUP пропатчить текущей страницей такой-то номер блока, такое-то смещение" (для одной метки может быть много постов!!!), а вычисления запретить
- если нашли числовую метку, то спокойно используем её в выражении
- если нашли адресную метку, то пишем точку релокации "номер блока метки, смещение"
- если ищем страницу метки и нашли метку, то пишем точку релокации "номер блока для far вызова"
как сделать вычисления размеров блоков кода? специальная директива-скобка?
как сделать другие вычисления с адресами кода ($-1, адрес+константа (поля статических структур, спрайты...), патчи для call/jp/jr, старшие и младшие части адреса (бывают с прибавлением константы), пересчёт адресов в другую область памяти, выделялки адресов по другим адресам(числовые?))?
как сделать org $-1? специальная директива, которая идёт в объектник?
алигны в nearcode запретить, т.к. смещения после них непредсказуемы? лучше алигн - новый блок!
)
лоадеру(линкеру) надо будет сначала прочитать список всех блоков, назначить им адреса, потом уже читать данные релокации и поток данных

если обращения к far будут через пересчёт страниц, то можно с помощью свопа запускать 128к программу на 48к
но это будет слишком медленно (мы не можем заставить часто общающиеся процедуры и данные лежать в одной странице)
хотя достаточно, чтобы хотя бы скомпилировать и запустить.
а вот практически мы можем разрабатывать 48к программы на 48к (свопиться будут редактор, компилятор, ассемблер целиком, кроме near части)


как сделать раздельную компиляцию? лоадер должен одновременно грузить из нескольких объектников? или порядок не важен, можно по очереди?
и должен быть указан весь список, что загружать


как выдавать текст в компилятор?
а) текст в памяти (не годится для 48к), организовать постоянные пайпы, а символы выдавать из редактора
б) текст в разделяемых файлах (но тогда компилятор должен уметь компилировать текст задом наперёд, что нереально)
б2) текст в разделяемых файлах, а символы выдавать из редактора согласно скрипту
стримить задом наперёд так же, как при печати текста на экране - установить указатель в файле (не от физического конца, а от логического, т.к. нельзя обкусывать конец файла при уменьшении), прочитать килобайт, использовать килобайт. при этом файл буферизован системой в памяти.
где хранить логическую длину файла1 и файла2?
при выходе из редактора склеивать файлы? медленно? если писать поверх хвоста файла1, то нельзя уменьшить его длину
в) текст в одном разделяемом файле (работа с большими файлами медленная и невозможна на 48К, а у нас есть тексты >40K)
г) общая задача доступа к тексту, которой пользуются и редактор, и компилятор?
д) реализовать в системе тип файла с добавлением в начало - вторая половина текста в нём?

а как при всём этом использовать include? нужно вставить препроцессоры-секвенсоры перед недолангом и перед недоасмом?
но как К НИМ приделать произвольное количество пайпов? создать при создании секвенсора на основе проектного файла?
для асма можно сразу предусмотреть чтение двух файлов?

предположим, что тексты в памяти, инклюдов нет, а редактор сам по скрипту стримит тексты в компилятор, а тот в ассемблер (нельзя два выходных файла, надо один)
должен быть символ прерывания компиляции (чтобы начать заново при изменении текста)
и наверно заголовок (в простейшем случае без заголовка), а имя выходного файла назначить при открытии выходного пайпа
при этом incbin может работать через другой файл, который откроет сам ассемблер
а как прикрепить стандартную библиотеку и другие файлы на ассемблере? тоже через другой файл? но он может быть вложенный!!!

вся среда разработки - это одновременно запущенные:
- коммандер (~10K)
- редактор (~20K+буферы)
- компилятор (~25K+метки)
- ассемблер (~10K+метки)
- запущенная программа (может быть большая, например, компилятор)

если запускать такую систему на 48К, то:
редактируемый текст будет лежать в файлах
когда будут загружаться программы компилятора и ассемблера? во время загрузки проекта ещё рано, т.к. вся среда разработки не влезет в 48К
программы будут свопиться только целиком. когда?
свопиться может даже редактор! а как тогда стримить? и как прерывать стриминг клавиатурой? по идее компиляция должна быть по фону!!!
для маленьких программ свободное место в памяти должен занимать буферизованный осью текст. как сделать его своп при необходимости загрузить программу?

выгрузка-загрузка программ - дорогая операция, поэтому надо её избегать

начали стримить
кончился сегмент
если загружен приёмник, то переключиться на него
иначе: если есть память (как узнать? особенно если программа хочет ещё динамическую память!), то загрузить приёмник, иначе выгрузить сегмент в физический файл
закончили стримить (что за сигнал?), выгружаем источник, загружаем приёмник



в 48К конкурируют за одну память:
нефиксированные блоки near кода и данных
нефиксированные блоки far кода и данных (превращаются в near)
256b блоки файловых сегментов
256b? блоки системных файловых кэшей



Ядро содержит ячейки с фиксированными адресами:
текущая страница (word для 16M) (4 байта на страницу(2 номер задачи, 2 физ. номер страницы)=4*1024=4k, но раз word, то можно давать задачам физ.номера страниц и таблица физ.номеров не нужна)(положить таблицу принадлежности страниц задачам в страницу - она требуется только при заказе и освобождении памяти)
содержимое eff7 - менять через функцию системы!!!
содержимое fe? не менять никогда?
текущий экран (0/8) - менять через функцию системы!!! чтобы на ATM не обращаться к 7ffd каждый раз при переключении страниц

Переменные драйвера девайса (типа недопечатанной буквы) не могут храниться между вызовами драйвера, т.к. дальше может обратиться совсем другая программа. Печатать надо сразу строку, звук кидать сразу массивом, на диск писать килобайтами (передавать указатель) и т.п.


обращения к файлам должны быть сетевые (запрос и чтение раздельно)
должно быть открытие файла с невозможностью другим в него лезть
как сделать файл базы данных, в который можно добавлять данные, пока другие читают? периодически делать его копии?

символические ссылки - источник глюков (вход-возврат, рекурсия)
невидимые символы в именах файлов - источник глюков
чтобы имена с пробелами не оформлять особо, надо в комстроке передавать все имена как отдельные объекты

надо иметь возможность знать текущий путь



преимущества скрипта (компиляция по мере исполнения):
+можно делать отключаемый range check (это можно и в ЯВУ)
+можно делать отключаемую защиту памяти (это можно и в ЯВУ)
+возможно экономия памяти под код
+можно неограниченный объём кода
проблемы:
-как определить размер области нижней памяти, куда компилировать? заранее просчитать при загрузке?
-что если не хватит нижней памяти под единицу компиляции?

можно компилировать кусок и вставлять в конце вызов компилятора





драйвер винта на один сектор можно засунуть в критическую секцию.
а поиск в фате нельзя! как сделать мьютекс?
вызывающий должен повиснуть до тех пор, пока не освободится ресурс.
один мьютекс на все входы драйвера hdd! вызывать все через одну точку?
а) поллинг флага в вызывающей задаче (требует, чтобы все задачи в таймслайсе вызывались по очереди!)
б) вызов с выпадением и цикл в вызывающей задаче
в) шедулер ищет задачи, висящие в освобождённом мьютексе и при переходе на такую снова его закрывает
г) при освобождении мьютекса переход на задачу по очереди в мьютексе


;Вход в засемафоренный участок
or a
semaphoreloop
call c,Sleep
ld hl,fsemaphore ;#fe - свободно, #ff - занято
sra (hl) ;#ff
jc semaphoreloop
;нужен Sleep с параметром-адресом семафора?


;Выход из засемафоренного участка
ld hl,fsemaphore
ld (hl),#fe

Если надо взять два семафора, то проверять надо свободность ОБОИХ СРАЗУ, а не сперва крутиться на одном, потом на другом, иначе возможен дедлок.

semaphore12loop
STARTCRITICAL
ld a,(fsemaphore1) ;#fe - свободно, #ff - занято
ld hl,fsemaphore2 ;#fe - свободно, #ff - занято
or (hl) ;если хоть один занят, то #ff
rrca
jnc semaphore12ok ;свободно
ENDCRITICAL
call Sleep
jr semaphore12loop
semaphore12ok
ld a,#ff ;занято
ld (fsemaphore1),a
ld (fsemaphore2),a
ENDCRITICAL







можно на АТМ запустить более 60 задач, если в одной страничке разрешать размещать много задач/модулей.
это требует наличия полноценной динамической памяти на уровне ос.
как заранее узнать, сколько физических страниц выделить под динамическую память, а сколько под страничную?
Это не нужно узнавать заранее. динамическая память организуется поверх страничной.
Если выделенной под неё страничной не хватает, то она просит ещё одну страничку, и т.д.
Это неизбежно, иначе может быть memory overflow при наличии свободных страниц.

Ещё один плюс АТМовской системы - ВСЕ модули могут стать равноправными (нет страничных и нестраничных),
но стек должен лежать в нижнем ОЗУ (его гораздо легче свопить).
Можно стек в странице кода, если есть страничные модули.

Попросить КОЕ сделать ещё одно окно страниц - в #4000? надо, чтобы этот порт не выбирался при ld a,%0x0xxxxx:out (#fd),a и при out #dffd, #fdfd, #1ffd.
Например, порт #3ffd (проще всего, т.к. есть #7ffd, #bffd, #fffd) или #9ffd.
Или лучше в #0000 (порт #FE при включенной ОЗУ в область ПЗУ), это даст плюсы:
1. возможность адаптировать is-dos chic (включать не 128-ю страницу в #0000),
2. не требуется сложное условие на адрес порта (можно #1ffd),
3. начальный номер 0 (или даже неопределённый) - проще, чем 5, 4.
возможность произвольной страничкой кода выводить из произвольной страницы данных на 0-й экран, причём возможность выводить на произвольный экран из произвольной страницы сохраняется.
Минус в том, что нельзя делать рамдиск vtrdos переключением страниц в #4000, но это и не нужно.
Проблема с выполнением кода в #3dxx, но можно использовать верхнее окно для кода, а нижнее для данных, и проблемы не будет.

Для сверхBQ под SOS: при окне в #0000 или #8000 нет возможности
убрать оба экрана из адресного пространства (чтобы было 32к под ос+стеки+
таблицы приложений+драйверы...). Этого можно добиться флагом "5-я/1-я стр. в #4000". Связать этот флаг с флагом "ОЗУ в #0000"?

При окне в #4000 такой проблемы нет, но ниж. память делится на 2 куска по 16к, это неудобно.
Переписывать программы так, чтобы данные в #0000, неудобно (много где есть inc h:call z,... и др.). Привычная среда для задач - при окне кода в #8000.
Свопинг стека запрещает запоминать SP в переменных, кроме разве что
специально помеченных. При модулях в страницах данных стек можно в стр. кода, но это ограничение на код.

Хотелось бы иметь 3 окна: в одном код, в другом данные, в третьем экран, а в оставшейся части адресного пространства - ОС+драйверы.
Чтобы драйвер экрана мог выводить и из стр. кода, и из стр. данных, без переключения страниц.
Или даже чтобы прямо выводить на экран без драйвера данные из любой страницы.
Проще всего адаптировать готовые программы (даже BQ), если ОС в #0000 (фиксированная стр.), экран в #4000, код в #8000, данные в #c000.
Если 2 окна, то шлём в драйвер экрана последовательность операций через пайпы.

Как освобождать память задачи при выходе? Подзадача не должна этого делать, если она внутри родительской задачи. Подзадача не может быть внутри? Сделать разделяемые сегменты кода для fork можно только при НДП (реально надо только vfork). Хотелось бы окна и для переменных, и для стека... (итого: 1. ОС, 2. экран 8К, 3. код текущего модуля (может быть разделяемым), 4. переменные текущего модуля, 5. страничные данные по 16К, 6. стек процесса, [7. модули ОС]). Для этого надо 7 окон по 8К (желательно с дробных страниц) и одно 16К. Сегменты кода и ОС должны быть защищены от записи; желательно отдельно отслеживать аппаратно переполнение стека; если страницы дробные, то при защите надо учитывать заявленный размер окна.


список свободных областей для динамического ОЗУ можно хранить прямо в этих
свободных областях. В начале каждой - структура:
WORD size
WORD nextpg
WORD nextaddr
Хотя желательно свободные области прошить ещё и на несколько списков по группам размеров.
Основной список, отсортированный по адресу, нужен для склеивания соседних блоков.
Как убрать блок из списка? Нужно переставить ПРЕДЫДУЩИЙ указатель (т.е. он указывал на него, а будет указывать на следующий).
А как перешивать списки по размерам? Просто добавлять новый блок в начало "списка по размеру".


для эффективного свопа на диск требуются счётчики обращений к страницам, весьма желательно - с таймстампами.
Это весьма замедлит переключение страниц.

надо задавать приоритеты не только для времени, но и для дисковых операций, и для сети


В UNIX проблема с учётом кол-ва открытий файла и каталога из-за fork.
Сделать не fork, а запуск программы с диска? (это замедлит создание кучи тредов)

для потоков надо общий код и данные, но разный стек.
либо надо переключать стек (может не хватить страниц, если потоков слишком много), либо задавать размер и защищать (может не хватить окна стека, если потоков слишком много).
для ЯВУ защищать надо только от stack overflow и только при рекурсии (проверять в самих вызовах), underflow не будет никогда, а использование стека внутри процедур можно контролировать в компиляторе. Но глубина стека в самой оси может меняться, особенно с разными драйверами - это проблема!


16-битный таймер переполнится через 22 минуты. нужен 32-битный?




Зачем при двух задачах 100% загрузки проца чередовать их? если ждать окончания одной перед пуском другой, то это удобнее и для юзера, и быстрее.
главное чтобы задачи с не100% загрузкой при этом не замораживались

Если планировщик имеет сложность O(1), то списки процессов удобно хранить в верхней динамической памяти.




Система в нулевых адресах:
Плюсы:
+ Удобство перехода из программы в ОС (ядро сейчас так и написано)
+ Удобно искать таблицу высших страниц (эта таблица - в странице, выделяемой драйвером памяти для своих целей); иначе - только через резидент либо привязку к адресам (причем инициализировать придётся до запуска планировщика)
+ Обработчик прерываний не требует места в основном ОЗУ
+ Можно использовать RST (его можно использовать для аппаратной защиты!)
(имитировать CP/M нельзя)
Минусы:
- Несовместимость с пентагон 128 (а надо ли?)
- Требуются системные процедуры включения и выключения этого режима (для драйвера дискеты), где их расположить?
а) в резиденте (нужен резидент)
б) в странице драйвера диска (кто их туда положит?)
в) страница драйвера диска = страница драйвера памяти (это загрузчик системы, напр., DNA OS - нижний уровень SOS)
г) а надо ли? можно всё самому написать, пускай лежит в странице (а как сохранять/загружать из страниц?). вообще дискеты не нужны в многозадачной ОС.

Дисковые операции надо совместимые с MSX-DOS и/или CP/M.
Нужна возможность перенаправить поток из консоли/порта в файл и наоборот


rpm -qa | more
ls -l `which su`

Нужен SManager.




Гуй имеет самый высокий приоритет.
таймер(в составе системы) и клавиатура(задача) шлют события (ontimer, onkeydown, onkeyup) гую. Гуй обрабатывает системные нажатия, переключает фокус (только те проги, у которых есть окно/экран)
и рассылает события прогам.
Клавиатуру - проге, которая в фокусе и драйверу текущей языковой раскладки (её переключает гуй), сообщения драйвера раскладки (keypressed)
транслируются проге в фокусе.
таймер - драйверу курсора для драйвера экрана (еще можно внутри использовать для эффектов и вочдогов, например)

Экран, память, диск (файловая система) висят на API.

клавиатура шлет событие гую [для перехвата нажатий надо не давать другим программам читать их! надо все через gui, а он организует цепочку вызовов - если одна программа не поняла клавишу, то вызов следующей]

API работает так:
Программа делает OpenDevice и получает указатель на драйвер девайса (или на его керналь?)
Девайс не должен давать себя убить/сменить штатными методами, пока все проги не отвалятся от него (через CloseDevice)
Программа может слать сообщение драйверу девайса - но тогда очередь экрана, например, быстро переполнится (нет проблем, если переход в случае переполнения)
Программа может слать сообщение драйверу девайса в очередь с переходом на процесс девайса (если он в таймслайсе, самой падать в таймслайс и повторять до таймаута)
Программа НЕ может слать сообщение драйверу девайса напрямую, вне очереди, с переходом на процесс девайса (тогда в том же девайсе нельзя использовать очередь - это приводит к тому, что нельзя обращаться к этому драйверу в ISR)

Программа может? вызывать функцию через керналь - самомодиф.код&обращения к хитрым ресурсам можно как местную критическую секцию; когда надо снимать/заменять драйвер - ставить пустышку в кернале; !!!проблема в том, что пока прога ждёт семафора, ей могло прийти миллион мессаг, а она их не поймает, пока не придёт семафор и не выполнится драйвер!!! а в ISR к таким драйверам обратиться нельзя (разве что если у двайвера два механизма - вызовы и сообщения, что нарушает FIFO-порядок операций в нём).

Программа НЕ ЗНАЕТ, существует ли задача, куда идут сообщения. Её могли закрыть, и сообщение должно просто игнорироваться. Однако как быть, если создали другую задачу с тем же номером (номера увеличиваются с каждым созданием, всего их 32768)?

Если при посылке сообщения очередь превысила лимит, то ставим текущую задачу в таймслайс и переходим на задачу, которой шлём (если она не в таймслайсе).
а если она в таймслайсе, то ставим посылающую задачу в таймслайс (так, чтобы на выходе из него она снова попыталась послать сообщение) и переходим на задачу, которой шлём. Если при этом поставить признак в описателе драйвера (или в шедулере): "после него переходить на задачу такую-то", тогда выход из цикла МОЖЕТ произойти быстрее, но МОЖЕТ возникнуть дедлок вызывающий-вызываемый, когда вызываемый ждёт сообщения от третьей задачи.
При этом методе буфер никогда не переполнится.
НО! если повиснет одна задача, повиснет вся система.
поэтому нужен таймаут в SENDMSG, по истечении которого забить на сообщение и вернуться в вызывающую программу.
подсчёт времени - по системному инкрементирующемуся в ON_INT (не в ISR!!!) счетчику.

Две очереди сообщений (например, в терминалке сериал и клавиатура) ничего не дадут - если пропускать
клавиатуру до окончания обмена по сериалу, можно ждать реакции на кнопку МИНУТАМИ. В одной очереди задача
решается либо созданием отдельного потока для сериала (защиты памяти нет - можно из потока в поток передавать указатели; в случае чего можно всю информацию через сообщения), либо затыканием сериала при обработке долгих событий.

Задача не должна крутиться в цикле, иначе не сможет обновлять свое окно при перетаскивании.
Что будет, если посреди обновления окна сменились координаты окна?
Как сделать, чтобы окно обновляла система? задача выводит в bitmap и дает сигнал системе? но в винде при перемещении окна перемещается старое содержимое.
Можно сделать в задаче обновление окна отдельным потоком. Т.е. во время , например, работы с диском (долгое ожидание) окно активно. Если окну послали сообщение "закрыть", то оно шлёт сообщение основному потоку о том же и закрывается после успешного закрытия того. Если основной поток сам решил закрыться (например, по F10), то он шлёт сообщение потоку окна о том же и закрывается после успешного закрытия того.
Если у задачи нет окна, то ей достаточно одного потока.
А если окон много? Основной поток должен рассылать всем (главному окну - только если основной поток решил закрыться сам, а не подчиняется приказу окна) и ждать их, прежде чем самому закрыться.

Ограничение системы только на событиях: нельзя использовать один ресурс двумя драйверами одновременно (например, нельзя DMA на экран - впрочем, можно наплевать на заполнение экрана другими задачами и работать через обычный DMA: если full screen, то монополия, а если в окне, то другие задачи в этом окне рисовать не будут)

Например, игра будет работать так:

IQ(высокий приоритет):
0. Подписаться на таймер и клавиатуру.
1. GETMSG.
Если таймер:
T1. считать игровую логику;
T2. если конец игры, то послать об этом сообщение задаче MAIN.
T3. перейти на 1.
Если клавиатура (это можно в MAIN):
K1. обновить матрицу клавиатуры/джойстика для игровой логики;
K2. перейти на 1.
Если "закрыть":
E1. закрыть себя с ответом автору сообщения "закрыть".
Иначе перейти на 1.

MAIN:
0. ...
1. GETMSG.
Если в режиме игрового цикла
{
Если "экран готов":
R1. плюнуть в экран сообщение "очистить невидимый экран".
R2. взять игровой мир (можно прямо из памяти задачи IQ, как в BQ)
R3. в цикле плевать сообщениями в экран - рисовать в невидимом экране (можно объединить с R2, если структура игрового мира не даст при этом глюков - в BQ не даёт).
R4. плюнуть в экран сообщение "переключить экран".
R5. плюнуть в экран сообщение "ответь, когда готов".
R6. перейти на 1.
Если "конец игры":
O1. плюнуть в IQ сообщение "закрыть".
O2. включить режим конец игры.
O3. перейти на 1.
Если "закрыть":
E1. запомнить автора сообщения.
E2. плюнуть в IQ сообщение "закрыть".
E3. включить режим закрытия.
E4. перейти на 1.
Иначе перейти на 1.
}
Если в режиме закрытия
{
Если IQ прислал "закрылся":
Q1. закрыть себя с ответом автору сообщения "закрыть".
Иначе перейти на 1.
}
Если в режиме конец игры
{
Если IQ прислал "закрылся":
Q1. включить режим меню.
Иначе перейти на 1.
}
Если в режиме меню
{
Если "закрыть":
Q1. закрыть себя с ответом автору сообщения "закрыть".
Если ...
...
Если ...
B1. включить режим игрового цикла.
B2. создать IQ с высоким приоритетом.
B3. перейти на 1.
}

можно предусмотреть процедуру закрытия, замораживающую программу, которая послала другой просьбу о закрытии, но GUI ею пользоваться не может.

если нужны GETMSG без задержек, если сообщений нет, то выход - функция ожидания готовности драйвера экрана, которая в общем случае работает так:
1. программа шлёт драйверу сообщение "ответь, когда готов" и идет на цикл приёма сообщений;
2. когда драйвер получает это сообщение в своём цикле приёма сообщений, он посылает отправителю сообщение "экран готов" и опять идёт на цикл приёма сообщений;
3. программа реагирует на сообщение "экран готов".


Игра типа Wolf/Quake должна работать по принципу клиент-сервер.
Клиент раз в кадр читает клавиатуру+мышь (или файл демо) и посылает её серверу в очередь.
Сервер читает очередь и обрабатывает ИИ и движения игрока. (Даже если попадётся кадр > 70000t, сохранится предсказуемость.)
Возвращает очередь координат (состояние игрока + координаты и внешний вид каждого активного/видимого объекта),
это можно делать по ходу обработки игрока и объектов.
Сервер может быть удалённым (например, General Sound).
Второй процесс клиента читает очередь координат и строит картинку.
Если пришло несколько кадров с координатами, брать последний (читать их поверх друг друга, пока в конце чтения кадра не будет EOF).
Если клавиатура буферизована, то можно один процесс клиента
(после построения картинки читать все накопившиеся события клавиатуры+мыши и передать их серверу).
Можно ли обойтись без проверки EOF и лишних кадров? После построения картинки посылать серверу готовность читать (в отдельную очередь)?
Он может её принять (всё-таки с проверкой EOF) не вовремя, хотя в среднем будет выдавать сколько надо кадров (если готовность не очередь, а флаг, то меньше кадров, чем надо).

По идее нужно обеспечить плавность движения независимо от сервера (это потребует ИИ для каждого персонажа в клиенте),
а с сервера выдавать данные можно не каждый кадр
(это не помешает засчитывать попадания, хотя ухудшит игроку возможность определять, где кто в данный момент).




Программа в цикле обработки сообщений (она может иметь несколько таких циклов) вызывает процедуру GETMSG, которая переходит на шедулер.
50Hz выдает событие "таймер", ставит флаг таймслайса для текущей задачи и переходит на шедулер.
Шедулер делает следующее:
Если есть хоть одна задача с приоритетом реалтайм, которой пришло событие, то удвинуть её в конец списка реалтайм (или переместить курсор на следующую в списке) и перейти на неё.
Если есть хоть одна задача с приоритетом нормал, которой пришло событие или у которой флаг таймслайса, то удвинуть её в конец списка нормал (или переместить курсор на следующую в списке) и перейти на неё.
Если есть хоть одна задача с приоритетом идле, то удвинуть её в конец списка идле (или переместить курсор на следующую в списке) и перейти на неё.
Иначе HALT.
Можно сделать 256 приоритетов: все в одном списке отсортированы по приоритетам.
В каждом описателе стоит указатель на начало списка со своим приоритетом.

везде, где стоит контекст задачи, ее стек, ее страница и не включен флаг шедулера, можно считать, что выполняется задача

обработчик NMI - переход на ON_INT (можно с флагом, чтобы легче определить NMI?)

при буфере 54 байта (АТМ) можно принимать RS232 по таймеру 50Hz со скоростью до 23040 (делитель 5) включительно

надо чтобы RS232 по таймеру и по родному прерыванию можно было юзать одинаково




Если все сообщения для всех задач в одной куче, то плохо:
список лежит в зацикленной области памяти, и даже если каждая задача вовремя забирает свои сообщения, то список рано или поздно будет физически испорчен (будет дырявый, но тем не менее новое сообщение налезет на старое)

Поэтому надо список сообщений у каждой задачи свой, в её главной странице

у каждой задачи:

Struct app
Byte priority ;приоритет
Byte flags ;флаги (factive, ftimeslice, fusetimer)
Word mainpg ;главная страница задачи (там очередь сообщений)
Word curmsg ;адрес текущего сообщения этой задаче
Word endmsg ;адрес конца очереди сообщений этой задаче
Word curpg ;текущая страница
Word sp ;текущий адрес стека (можно хранить в ТЕКУЩЕЙ странице, но это бессмысленно)
Word next ;указатель на следущую задачу (следующая за выполняемой внутри того же приоритета)
;[номер задачи, которая ждёт готовности этой задачи - реально это список и привязан к мьютексам/семафорам]
EndStruct

шедулер делает так:
ищет первую активную задачу от начала списка задач (т.е. от самых приоритетных)
если она имеет тот же приоритет, что "следующая внутри приоритета", но расположена раньше - пропускаем
если нашли, ищем "следующую внутри приоритета", запоминаем ее, входим в задачу.

можно ускорить:
при входе в задачу рекорд="следующая внутри приоритета"
при посылке сообщения (в таймслайсе условие не выполнится) смотрим: если активируемая задача имеет приоритет выше, чем текущая(или чем "следующая внутри приоритета", что эквивалентно) задача и при этом стоит раньше, чем рекорд, то рекорд=активируемая задача
шедулер начинает просмотр списка задач с задачи рекорд.


очередь сообщений лежит в зацикленной области памяти (256 байт)
и состоит из сообщений следующего вида:

Struct msg
Word type ;тип сообщения (2 б)
Word sender ;автор сообщения (2 б)
Array param,12 ;параметры (2-байтные) - число фиксировано!!! 6 шт? (тогда структура=16 байт)
EndStruct

Очередь сообщений может быть в любом месте страницы, выравненном по 256 байт. Т.к. одна задача может иметь много потоков, и расточительно под каждый выделять страницу. Но имя задачи достаточно одно - по фиксированному адресу (#C000)? Тогда Ctrl+Alt+Del будет показывать подзадачи как "имя;адрес очереди сообщений"

Можно динамическую очередь сообщений (с указателем на следующее сообщение),
чтобы тело сообщения лежало в памяти отправителя.
Но как отправитель (например, клавиатура) будет выделять память под сообщения,
если она не ждёт, когда их заберут?

Windows.pas:
  tagMSG = packed record
    hwnd: HWND;
    message: UINT;
    wParam: WPARAM;
    lParam: LPARAM;
    time: DWORD;
    pt: TPoint;
  end;

Types.pas:
  TPoint = packed record
    X: Longint;
    Y: Longint;
  end;
То есть достаточно тип + 4 параметра + время(зачем?)






было бы круто файлы настроек для программ поддерживать на уровне системы, чтобы программы их видели как переменные. на выходе обновляли файл, если переменные изменили.





[20:15] <alone> вот что я вижу в качестве оси на ZX:  MagOS (по сути много терминалов) + многозадачность + драйверы
[20:16] <alone> переключение по нми (надо Alt+Tab, по факту отжатия, т.к. переключение между трдос-задачами будет долго)
[20:16] <alone> трдоссофт запускается в однозадачном режиме
[20:16] <alone> после него надо всю память обратно восстанавливать
[20:17] <alone> в многозадачном режиме каждой задаче выделяется 47K непрерывно с адреса #400 + сколько угодно страниц (сколько запросит)
[20:17] <savelij13> дык это не ось а манагер запущенных прог )
[20:17] <alone> нееее, можно будет делать всё то же, что в винде, только без окошек
[20:18] <alone> например рар рарит, плеер плеит, а мы пока прогу сочиняем
[20:18] <savelij13> дык чо нуно пилить симбос? тока я хз как обойти вдп
[20:18] <alone> вот мне кажется что симбос ненужен
[20:18] <alone> трудно его будет освоить юзерам
[20:19] <alone> потом как-нибуть
[20:19] <savelij13> почему?
[20:19] <alone> потому что там совсем другое программирование
[20:19] <alone> другая концепция экрана например
[20:19] <alone> а тут пишешь как под обычный атм, только вместо п/п переключения паг вызываешь ось
[20:19] <savelij13> бугага. чо то я ненаблюдаю программеров. даже альтернативы ерс нету и не придвидица )
[20:20] <alone> я хотел писать ось в своё время, потому что мне не хватало непрерывного места под код
[20:20] <alone> 47К можно даже на си писать
[20:20] <alone> можно даже 63К, если задача не юзает данные в пагах
[20:20] <savelij13> дык есть же мсхдос к тому же обратносоместимый с цпм
[20:21] <alone> там нет многозадачности даже а-ля магос
[20:21] <alone> но вызовы мсхдос можно сделать
[20:21] <savelij13> а кто мешает ее прикрутить?
[20:21] <alone> 100% совместимые
[20:21] <savelij13> тем более если нми то причом там будет мсхдос?
[20:22] <alone> файловые операции
[20:35] <alone> даже с адреса #100 надо код, чтобы уж совсем цп/м подобие
[20:35] <alone> по крайней мере мне кажется что это самая жизнеспособная концепция. я много разных передумал
[20:36] <alone> по сути в интерфейсе цп/м надо добавить команды "заказать пагу", "включить пагу", "освободить пагу"
[20:37] <alone> sleep - через getch
[20:37] <alone>  потом можно усложнять
[20:37] <alone> проблемы в реализации такие:
[20:38] <alone> как передавать данные в драйвер фата
[20:38] <alone> как сделать его реентерабельным или же запирать семафором



[16:21:03] <Leaf44119> Публичный API - не воробей, опубликуешь - не уберёшь
[16:21:27] <Leaf44119> Так вот почему апи так разрастаются - убирать-то ничего нельзя


Надо под задачу или драйвер 63к либо 47к+окно.
Как передавать данные в драйвер?
а) Указателем со страницей?
б) Или указателем и включить страницу?
в) Или стеком и включить страницу?
Где начальное положение sp? 0 для 63к, ц000 для 47к?
Никакого драйвера памяти! Мму в системе тоже не меняется
Текст.ред.: делить по курсору, динамически создавать и хранить разметку текста для каждой страницы перед курсором, чтобы мотать назад

Как писать проги под атм видеокарту, чтобы совместимо с осью? Даже если еще 31к проги: Вызовы оси можно перекрывать, но не им1!!! Делать им2 для 31к прог? И все вызовы другие? Или перед вызовом заставлять ставить правильную пагу в 0?

Выкинуть в атмконф: ефф7, мультиколор, 16ц, озу в 0, старшие биты 7ффд, 7 мгц, 320 строк, интерфейс с процом на плате, арбитр (читать сразу всю строку в паузах з80 или остановив некстз80) - оставить только некстз80. А как же zxbus?



