Выпустил обновление. Из изменений: небольшая оптимизация при сохранении контекста при прерываниях. Добавлена возможность пуска новых и завершения старых потоков. Для этого используются две новых функции: KeStartThread и KeExitThread.
Перед пуском нового потока необходимо инициализировать в памяти структуру состояния потока, указать адрес стека потока и разместить на его верхушке точку входа. Адрес структуры нового потока передается функции KeStartThread в регистре IY. Новые потоки находятся в состоянии готовности. Функцию KeStartThread можно вызывать только на IRQL=PASSIVE из какого-нибудь потока. При этом, если приоритет нового потока выше, чем у текущего - то текущий поток будет сразу вытеснен, а управление - отдано новому потоку.
Поток может завершиться, вызвав функцию KeExitThread по Call или Jp, эта функция не возвращается. Управление передается следующему по приоритету потоку, находящемуся в состоянии готовности. Последний поток с самым низким приоритетом (system idle thread) не должен завершаться или входить в режим ожидания, иначе происходит сбой.
Изменен способ хранения структур потоков в памяти. Если раньше это был массив структур, длина которого известна во время компиляции, то теперь это односвязный список, элементы которого отсортированы по приоритету в порядке его убывания (это соответствует возрастанию числа, кодирующего приоритет). По-прежнему требуется, чтобы все потоки имели разный приоритет. В сортированном односвязном списке добавление и удаление - дорогие операции, имеющие сложность O(n), однако пуск и завершение потоков происходят редко, поэтому сложность не критична.
Я не делал любимую многими функцию "TerminateThread" - прибитие одного потока из другого, по той причине, что если прибиваемый поток находится в состоянии ожидания - то его необходимо удалить из списка потоков, ожидающих некий объект. Но поскольку список этот односвязный - то удалить из него элемент невозможно. Надо или хранить в структуре потока указатель на начало списка, или делать двусвязный список. И то, и другое затратно по памяти и по быстродействию операций ожидания и выхода из ожидания, а толку от TerminateThread без защиты памяти мало. Чтобы снять заглючившую задачу, недостаточно прибить поток. Надо еще освободить его ресурсы, а если он перепахал память - то пиши пропало.
Следующий шаг - динамический приоритет потоков. Здесь опять потребуется изменять структуры данных системы. Чтобы изменить приоритет потока, нужно изменить его положение в списке потоков, отсортированном по приоритетам. Во-первых для списков такое перемещение само по себе затратное - O(n), а с односвязным списком вообще все печально. Фактически придется как бы прибить поток и запустить его заново, и это будет очень тормозно. Возможно, вместе с динамическим приоритетом потоков я добавлю возможность иметь потоки с одинаковым приоритетом и Round-Robin. Для этого придется радикально поменять формат "базы данных" диспетчера, сделав его более похожим на то, что используется в Windows NT.
Ну а с динамическим приоритетом станут возможными, наконец, мутексы.
---------- Post added at 04:25 ---------- Previous post was at 04:16 ----------
Планировщик-то вызывается, но потоки не переключаются каждое прерывание. Quantum для Round-Robin может составлять величину, превышающую интервал между прерываниями.
Внутри системы это приводит к вызову WaitForObject. Все это реализовано в моем диспетчере.
Вот как раз аппаратные прерывания от мышек - это по-моему плохая идея. Достаточно опрашивать мышку с частотой обновления экрана, как это сделано на Spectrum и Amiga. Все равно курсор мыши на экране нельзя сдвинуть быстрее, чем отобразится следующий кадр. В качестве бонуса получаем очень плавное перемещение курсора. Кто видел живую Амигу - не даст соврать.