Оберон-клуб «ВЄДАsoft»

Твердыня модульных языков
Текущее время: 29 июн 2025, 22:27

Часовой пояс: UTC + 2 часа




Начать новую тему Ответить на тему  [ Сообщений: 9 ] 
Автор Сообщение
СообщениеДобавлено: 12 ноя 2014, 22:37 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Reobne писал(а):
Решил разобраться как работает режим IM2. Не получается красиво запустить на прерывание свою процедуру. Добавлять инициатор бейсика Basic_Init_IM2_Sound или Basic_Init_IM2_EX - некрасиво.
Эта фича давно назрела. :) По задумке режим работы программы задаётся в отдельном конфигураторе BasicCfg.h, и такой отдельный конфигуратор можно создать для каждого проекта. Или же воспользоваться конфигурацией по умолчанию. Выглядит он так:
Код: "C"
/* Interrupt mode of your program: DI, IM0, IM2. */
/* For IM 0 mode use SDCC option --reserve-regs-iy */
//#define MODE_DI
//#define MODE_IM0
#define MODE_IM2
Так сделано для упрятывания режима работы, изоляции этой сущности от, возможно, малоопытного бейсик-программиста. Как было бы лучше? — не знаю. Возможно, надо добавить в интерфейс и явные процедуры InitDI, InitIM0, InitIM2, но тогда придётся добавлять и QuitDI, QuitIM0, QuitIM2, а также PAUSE_DI, PAUSE_IM0, PAUSE_IM2, BEEP_DI, BEEP_IM0, BEEP_IM2 и т.п.

Предлагаю использовать для режима IM 2 процедуру Basic.IM2PROC(proc: PROCEDURE), которую я добавил сегодня. Пример — проигрывание мелодии в фоне (на прерываниях) параллельно с основным циклом исполнения прикладываю. Сама соль в этих строках:
Код: "OBERON"
  1. PT3x0A.Install(ace); (* Установим мелодию в плеер *)
  2. Basic.IM2PROC(ProcIM2); (* Установим процедуру-обработчик прерывания IM 2 *)
  3. Best40Test; (* Пользовательский код исполняется, музыка уже играет параллельно *)
  4. PT3x0A.Stop; (* Гасим плеер *)

Вместо ProcIM2 могла бы запросто быть процедура PT3x0A.Play, но в самый последний момент выяснилось, что она запрещает прерывания. Поэтому процедура-обработчик прерываний выглядит у нас так:
Код: "OBERON"
  1. PROCEDURE ProcIM2;
  2. BEGIN
  3. PT3x0A.Play; Asm.Code("EI");
  4. END ProcIM2;
Трудность в том, что использованный мною плеер PT3x0A должен быть выровнен на границу 256 байт, так что после компиляции код проверяет выровнен ли он, и если нет, то выводит на сколько нужно выровнять. Но думаю решить эту проблему использованием PT3-плеера Сергея Бульбы.

Reobne писал(а):
Получится, что если ещё добавлять очередную процедуру опроса (например мыши), то всё снова перековеркивать.
Ну да, с IM 2 не всё так просто. :) В любом случае, обработчик прерываний нужен централизованный, впрочем, он может быть диспетчером, самолично вызывающим обработчик для опроса событий от мыши, оформленный в виде обычной процедуры.

Reobne писал(а):
Править адрес (65525), тоже костылясто.
Напомни, плиз, что это за адрес.

Reobne писал(а):
Как правильно инициировать музыку?
В плеере PT3x0A так :
Код: "OBERON"
  1. PT3x0A.Install(melody); (* melody – это адрес мелодии, переменная типа PT3x0A.Melody *)

Reobne писал(а):
Как добавить бинарный ресурсный файл?
Проще всего — с помощью Basic.DATA* (примеры есть в примерах к ZXDev).

Reobne писал(а):
Насколько позволительно добавлять в интерфейс модуля бейсика новый "оператор" PLAYFX(N)?
Позволительно конечно, но лучше в виде отдельной библиотеки. Так будет правильнее: библиотека Basic — для возможностей, реализованных в ZX-Basic'е. Вообще-то и процедуру IM2PROC стоило бы выделить в отдельную библиотеку, но уж больно её реализация привязана к Basic.Init_IM2.


Вложения:
TestPT3IM2.zip [8.11 КБ]
Скачиваний: 1590
Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 13 ноя 2014, 04:22 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Оказалось, что процедура PT3x0A.Play портит содержимое альтернативных регистров, а процедура Best40.PSCALER из-за этого неправильно работает. Поэтому наш обработчик прерываний, который этого не учитывал, будет выглядеть так:

Код: "OBERON"
  1. PROCEDURE ProcIM2;
  2. BEGIN
  3. Asm.Code("EX AF,AF");
  4. Asm.Code("EXX ");
  5. Asm.Code("PUSH AF ");
  6. Asm.Code("PUSH BC ");
  7. Asm.Code("PUSH DE ");
  8. Asm.Code("PUSH HL ");
  9. PT3x0A.Play;
  10. Asm.Code("LD IY,#0x5C3A");
  11. Asm.Code("RST 0x38 "); (* Таймер тикает, клавиши опрашиваются *)
  12. Asm.Code("POP HL ");
  13. Asm.Code("POP DE ");
  14. Asm.Code("POP BC ");
  15. Asm.Code("POP AF ");
  16. Asm.Code("EXX ");
  17. Asm.Code("EX AF,AF");
  18. Asm.Code("EI");
  19. END ProcIM2;
Вот так всё отлично работает! :) А вызов RST 0x38 нужен для того, чтобы процедура Basic.PAUSE(Basic.WaitAKey) ждала нажатия кнопки, а без этого просто висло.


Вложения:
TestPT3IM2_fixed.zip [8.2 КБ]
Скачиваний: 1686
Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 14 ноя 2014, 07:04 
Не в сети

Сообщения: 25
Zorko писал(а):
Reobne писал(а):
Править адрес (65525), тоже костылясто.
Напомни, плиз, что это за адрес.

Там лежит адрес, на который происходит прыжок при прерывании(в текущей реализации бейсик-либы). Каждое новое ... каждый новый "драйвер мыши" при инициализации, берёт этот адрес, на место его записывает адрес своего обработчика прерываний, по окончании которого не забывает перейти по взятому адресу.
Плохого много. На обработчике будет висеть много мусорного кода, сохранения-восстановления регистров. Затруднительно правильно деинициализировать какой-либо обработчик. Трудно задать порядок вызова обработчиков.

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


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 14 ноя 2014, 08:54 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Reobne писал(а):
Каждое новое ... каждый новый "драйвер мыши" при инициализации, берёт этот адрес, на место его записывает адрес своего обработчика прерываний, по окончании которого не забывает перейти по взятому адресу.
Плохого много. На обработчике будет висеть много мусорного кода, сохранения-восстановления регистров. Затруднительно правильно деинициализировать какой-либо обработчик. Трудно задать порядок вызова обработчиков.
Да, так нельзя.

При разработке для такой платформы как Спектрум трудновасто распределять ресурсы автоматизированно. Такие как прерывания или каналы музыкального сопроцессора. Взять проигрыватель музыки PT3x0A и твой плеер звука. Они ведь подерутся за канал A, нет? :) И получим какофонию вместо звука. Учесть всё невозможно. Значит будем делать всё это вручную. Обработчик прерываний будет писать сам юзер и пихать туда ну ровно то, что ему нужно. И в нужном ему порядке. И он же решит: сохранять ли альтернативные регистры и вызывать ли RST 0x38. Модули, работающие на прерываниях, будут экспортировать процедуру, которую нужно вызывать каждые 1/50 сек. Пусть эта процедура портит регистры и делает что ей угодно. Об этом будет заботиться обработчик прерываний. Ведь всё равно настоящего событийного (активного) режима работы на Спектруме не добиться. Всё равно придётся дёргать какой-то код для опроса событий от мыши, ведь в Спектруме событие само не вызывает прерываний.

Так что предлагаю отдать обработчик прерываний юзеру. И пусть у него болит голова насчёт расшаривания канала A. Т.е. пусть это будут учитывать используемые им плееры звука и музыки. А если понадобится другая логика, другой порядок обработки событий, юзерский код просто подставит новый обработчик вместо старого тем же самым вызовом Basic.IM2PROC(новый_обработчик).

Reobne писал(а):
Идеальный вариант диспетчера прерываний, это вызвать Оберон-код перед сборкой. Этот код должен видеть кто и с какими параметрами-константами вызывает регистрацию обработчика прерывания. И этот код, в этот момент, должен сгенерировать ассемблерный код для Z80 в соответствии с ситуацией.
Идею не совсем понял. Зачем генерировать ассемблерный код, и как именно?


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 14 ноя 2014, 14:53 
Не в сети

Сообщения: 25
Я вот тоже не понимаю, зачем от человека прятать то, что он должен настроить?
Если уж прячем, то нужно чтобы всё работало само, без настроек.
Если прерывания не нужны, нет ни музыки, ни таймера, ни паузы, ни стандартного опроса клавиатуры, то отключить их совсем. Один байт "DI" и всё.
Если нужен опрос клавиатуры, или пауза, то генерируется "IM 1"
Если-же Некоторое количество модулей хотят сами повисеть на прерываниях, например вызывают:
ZX_Interrupt.StaticReg(MyProc,0(* приоритет *),ZX_Interrupt.NeedPush_AFHL+ZX_Interrupt.NeedPush_BC+ZX_Interrupt.NeedPush_IX);
ZX_Interrupt - модуль отвечающий за спектрумовские прерывания
StaticReg,DynamicReg,UnReg - его функции регистрации обработчика.

Тогда формируется мощьный код, с формирование 257-байтной таблицы. Если вызывается DynamicReg, то по другому, и больше кода.

Может модуль узнать, какие его вызовы есть, и с какими аргументами?


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 14 ноя 2014, 18:41 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Reobne писал(а):
Я вот тоже не понимаю, зачем от человека прятать то, что он должен настроить?
Ну с этим всё просто. Сперва человек пишет как бы на ZX Basic, не парясь режимом прерываний. Потом, узнав что есть режим IM 2, человек задаётся вопросом: как его использовать? И узнаёт это. Для библиотеки Basic этого более чем достаточно, это даже больше, чем есть в ZX Basic. А для более изощрённых применений не используем библиотеку Basic, пишем свою с другой логикой.

Смысл конфигурирования внешним конфигуратором, вынесенным из исходника, открывается когда тебе нужно сгладить различия платформ. Чтобы исходник выглядел одинаково для всех платформ, а отличались только специфические конфигураторы. Так устроена игра Dark Woods. И, возможно, в библиотеке Basic смысл выделять режим прерываний в конфигуратор и сомнителен, поскольку Basic чётко машинно-зависимая, то уж в библиотеке Console его намного больше, потому что этот модуль относительно кроссплатформенный. Если брать Console в качестве хост-модуля (вместо Basic) — выбор режима прерываний не нужен для платформы Windows, но, если в этой же программе используется и музыка, он нужен для ZX.

Reobne писал(а):
Если прерывания не нужны, нет ни музыки, ни таймера, ни паузы, ни стандартного опроса клавиатуры, то отключить их совсем.
Пауза может разрешать прерывания, отрабатывать и опять запрещать. Так работает внутренняя процедура Basic_PAUSE_DI.

Reobne писал(а):
Если нужен опрос клавиатуры, или пауза, то генерируется "IM 1"
А если нужен опрос клавиатуры и пауза в режиме IM 2 (для музыки в фоне)?

Reobne писал(а):
Если-же Некоторое количество модулей хотят сами повисеть на прерываниях,
Вах, дарагой. :) Не пойму твоего упорства всё время представлять это с такой стороны: “модуль регистрирует обработчик и наслаждается своей автономностью”. Это из мира событийного программирования. Если некоторое количество модулей хотят повисеть на прерывании, пусть экспортируют свою внутреннюю процедуру-обработчик RunMe50Hz. А диспетчер будет сам дёргать эти обработчики. И напишет его сам юзер, не тратя лишних тактов. Так будет намного эффективнее для ZX. Лучше здесь просто не придумаешь. Централизованное сохранение регистров, вызов именно в нужном порядке (например, сначала опрос клавиш, потом музыка). И так будет относительно кроссплатформенно. Ты просто опишешь диспетчер IM 2 в ядре, отличающемся для разных платформ. И это ядро будет маленьким, оно будет только помогать сгладить платформенные различия.

Reobne писал(а):
например вызывают:
ZX_Interrupt.StaticReg(MyProc,0(* приоритет *),ZX_Interrupt.NeedPush_AFHL+ZX_Interrupt.NeedPush_BC+ZX_Interrupt.NeedPush_IX);
ZX_Interrupt - модуль отвечающий за спектрумовские прерывания
StaticReg,DynamicReg,UnReg - его функции регистрации обработчика.

Тогда формируется мощьный код, с формирование 257-байтной таблицы. Если вызывается DynamicReg, то по другому, и больше кода.
Да, можно сделать как ты предлагаешь. Я сделал как мне на тот момент казалось удобнее (или проще).

Ты хочешь сделать чтобы модуль мыши сам регистрировал обработчик, сам обрабатывал прерывания и всё это было бы достаточно локализованно, чтобы не влиять на работу других модулей? Это возможно, но это малоцелесообразно. Как я уже сказал, в ZX события не вызывают прерываний. Ты хочешь эту активность эмулировать, и пожалуйста, но меньше кода у тебя не будет.

Reobne писал(а):
Может модуль узнать, какие его вызовы есть, и с какими аргументами?
Модуль не может узнавать какие вызовы есть. Он просто может экспортировать наружу разные процедуры (вплоть до асмовых вставок), а уже сама процедура узнаёт какие вызовы есть, но только в рантайме конечно. Создав набор смартлинкуемых процедур можно добиться примерно того же эффекта, только будет не ZX_Interrupt.NeedPush_AFHL+ZX_Interrupt.NeedPush_BC+ZX_Interrupt.NeedPush_IX, а ZX_Interrupt.NeedPush_AFBCHLIX, ZX_Interrupt.NeedPush_AFBCDEHLIXIY и т.д. и т.п. Этого мусора будет там много, но другого способа сделать всё в compile-time я не знаю. И, опять же, всё это имело бы больше смысла если бы модуль для работы с мышью был кроссплатформенным. А так ориентация на Спек железная. Впрочем, я понимаю. Ты хотел бы просто прикрепить задачу к "активности" вызовом отдельной процедуры. И забыть про все проблемы. Но тогда нужна будет очередь вызова и действительно будет много мороки с описанием сохранения регистров. И в compile-time это можно сделать лишь каким-либо сишным препроцессором. Впрочем, надо подумать. Просто мне такое точно не нужно, надеюсь обойтись своим центральным обработчиком и вызовами в стиле RunMe50Hz. Ведь даже кроссплатформенный модуль для работы с мышью (его ZX-реализация) может экспортировать процедуру RunMe50Hz, о которой будет знать только ZX-ядро.


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 14 ноя 2014, 20:07 
Не в сети

Сообщения: 25
Zorko писал(а):
человек задаётся вопросом: как его использовать? И узнаёт это.

Надеюсь, это было сделано не для того, чтобы на форуме задавалось больше вопросов
Zorko писал(а):
И напишет его сам юзер, не тратя лишних тактов. Так будет намного эффективнее для ZX. Лучше здесь просто не придумаешь.

Для эффективности ZX, но не для эффективности работы пользователя-программиста. Ему придётся писать банальности. Писать, переписывать, искать ошибки, сомневаться.
Zorko писал(а):
Ты хочешь сделать чтобы модуль мыши сам регистрировал обработчик, сам обрабатывал прерывания и всё это было бы достаточно локализованно, чтобы не влиять на работу других модулей?

Нет. Я приложил картинку в екселе. Модуль мыши экспортирует свою функцию, а пользователь в "Бэкэнд для ZX" Регистрирует его в в модуле прерываний, с нужным приоритетом.
Код бэкэедов должен быть минимальный.
Конфигураторы, я представляю, как нечто чужеродное, сидящее внутри зелёных стрелок. То есть конфигуратор настраивает конкретную библиотеку, для конкретной машины, для конкретного проекта.

Reobne писал(а):
И, опять же, всё это имело бы больше смысла если бы модуль для работы с мышью был кроссплатформенным.
А то, что он кросспроектный, недостаточно? А ещё и кроссчеловеческий. Чем удобнее вещь, тем больше народу удачно её используют.


Вложения:
Комментарий к файлу: Первое приближение :)
Диаграмма1.zip [8.62 КБ]
Скачиваний: 1870
Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 14 ноя 2014, 21:10 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Можно сделать модуль, регистрирующий по требованию нужный обработчик:
Код: "OBERON"
  1. DEFINITION IM2Proc;
  2.  
  3. PROCEDURE UseMouse;
  4. PROCEDURE UseMouseAndMusic;
  5. PROCEDURE UseMouseAndSound;
  6. PROCEDURE UseMouseAndMusicAndSound;
  7. PROCEDURE UseMusic;
  8. PROCEDURE UseMusicAndSound;
  9. PROCEDURE UseSound;
  10. PROCEDURE Quit; (* Вызвать перед выходом, чтобы перейти в IM 0 *)
  11.  
  12. END IM2Proc.
Пользователь будет вызывать один из обработчиков, и каждая из этих регистрирующих обработчик процедур (конечно смартлинкуемых) создаст 257-байтную таблицу, подключит нужный обработчик и сама будет прекрасно знать какие регистры нуждаются в сохранении.

Трудность: всё равно потребуется явный вызов IM2Proc.Use* и IM2Proc.Quit, что неприемлемо для кроссплатформености. Всё равно понадобится ядро. Я не считаю это недостатком. Вполне нормальное решение.

Впрочем, для кроссплатформенностых (и даже кроссчеловеческих) применений и унификации можно сделать конфигуратор типа:
Код: "C"
#define IM2_MOUSE
//#define IM2_MUSIC
#define IM2_SOUND

Не обвиняй меня, пожалуйста, в закулисно хитром кодинге, нуждающемся в разжёвывании на форуме. Мне вот бы кто тонкости работы с AY рассказал, а то это для меня как китайская грамота. :) Я библиотеку Basic для игры "Дурак" делал, даже не думая в тот момент, что её будет юзать кто-то ещё. Но это не значит что она плоха, она вполне себе даже ничего, как на мой вкус.

Reobne писал(а):
Для эффективности ZX, но не для эффективонсти работы пользователя-программиста. Ему придётся писать банальности. Писать, переписывать, искать ошибки, сомневаться.
С этим я согласен, но ZX-программисту в любом случае придётся писать банальности, переписывать, искать ошибки, сомневаться. В библиотеках, как и в сборочных скриптах всё не предусмотришь. Если думаешь, что предусмотришь, попробуй предусмотреть, кто бы возражал. Но нет, всегда будут тонкости сопряжения и взаимодействия. Не нужно пытаться предусмотреть всё на свете. Нужно просто прятать излишнюю сложность, позволяя локализовать проблему как можно легче. Асм в Оберон-обёртке позволяет делать это лучше, чем без неё, мне так кажется.

Reobne писал(а):
Конфигураторы, я представляю, как нечто чужеродное, сидящее внутри зелёных стрелок. То есть конфигуратор настраивает конкретную библиотеку, для конкретной машины, для конкретного проекта.
Я представляю конфигуратор в виде редактора свойств и событий как в Дельфи. Но не против и зелёных стрелок, может со временем что-то такое и реализуем. Пока же у нас есть все воможности делать такие конфигураторы, просто без графического интерфейса, специализированного редактора свойств.


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 15 ноя 2014, 17:37 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
А что, пожалуй, можно выбросить настройку режима прерываний из конфигуратора. Basic.IM2PROC может сама создавать таблицу и делать всё по подготовке и активации режима IM 2. Ещё понадобятся какие-нибудь Basic.IM0 и Basic.DI, изменяющие режим прерываний. Трудности возникают с PAUSE и BEEP, в них придётся опрашивать текущий режим и реагировать соотв. образом: в режиме DI перед возвратом включать DI, в режиме IM 0 сохранять IY, в режиме IM 2 возвращаться с разрешённым EI. Просто больше кода будет.


Вернуться к началу
 Профиль  
Ответить с цитатой  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 9 ] 

Часовой пояс: UTC + 2 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
© VEDAsoft Oberon Club