🟰 При внесении этих изменений (ebp=0x01C71C80) я столкнулся с проблемой. Зарезервировать такие малые адреса не удавалось, т.к. они были уже заняты после инициализации DLL-ок типа USER32, MSVCRT и т.п. Пришлось выносить весь основной код в DLL и делать отдельно EXE-загрузчик, который резервирует нужные адреса и потом уже загружает основной код (динамически — LoadLibrary). Это сработало. Но требует тестирования на разных версиях винды.
Если будут проблемы, поменяю ebp на 0x03330000 (2x = 0x06660000, 3x = 0x09990000, 5x = 0x0FFF0000), забив на фичу с [ebp+ebp*8] 😏
С одной стороны, мне не хотелось выносить всё в DLL, но потом я решил, что это даже хорошо. Почему? Об этом в следующем абзаце.
⏸ Пытаясь сварганить интру, я подумал, что было бы здорово иметь возможность написать прототип на C/C++, заодно и потестить финальную скорость. И тут я понял, что вынос основного кода в DLL — это то, что нужно. Сделать прототип можно двумя способами:
🅰️ Создав DLL с экспортируемой функцией типа ParallelixEntry, передавая такую DLL в качестве параметра при запуске среды (вместо plx-файла — да, это расширение файлов с интрами, запоминайте).
🅱️ Создав EXE, который будет вызывать функции DLL среды (в первом варианте это тоже будет, конечно, только здесь нужно будет дополнительно вызывать функцию инициализации среды, передавая параметры командной строки).
За это я ещё пока не брался, конечно же.
⏸ Сделал возможность указывать только ширину экрана с автоматическим подбором высоты, исходя из соотношения сторон текущего видеорежима. Также сделал возможность устанавливать режим экрана по умолчанию (это либо режим, установленный при запуске, либо заданный параметром командной строки). А также половину разрешения этого режима, треть и четверть по каждой из сторон. Причём, при использовании этой возможности зачастую можно сэкономить 3 байта кода.
⏸ Ещё я решил, что нужно сделать API-функцию @InitVideoOrDisplayFrame, которая будет при первом вызове устанавливать видеорежим, а при повторном — выводить изображение. Экономия на лишнем вызове (@InitVideo и @DisplayFrame тоже останутся, не переживайте).
🟰 Наверняка, я сделал что-то ещё, но не столь существенное, поэтому забыл написать об этом.
Но самый прикол в том, что файл TODO растёт быстрее, чем я успеваю что-то делать (а посвящать проекту слишком много времени я тоже не могу).
❓ Ещё один вопрос меня терзает. Это неиспользуемые параметры. Сейчас некоторые функции могут принимать переменное число параметров (2-3 или от 1 до 4-х, например). Кол-во параметров определяется отдельными битами первого. Так вот, вся проблема в том, что в конечном счёте любой API-вызов сводится к вызову функции C++, где кол-во параметров не может быть переменным (не надо про variadic — там свои заморочки). Поэтому промежуточный код (который в случае фиксированного кол-во параметров сводится к jmp) проверяет биты, записывает в стек либо реальный параметр, либо пустышку, а затем делает вызов stdcall-функции C++ (с уже фиксированным числом параметров).
В случае сложных схем (где число опциональных параметров 3 и более, либо где параметр зависит от значения нескольких битов), код получается довольно громоздким (не один десяток инструкций). Да, ещё не нужно забывать, что выходов из таких функций должно быть тоже несколько, например, ret 4, ret 8, ret 12, ret 16 :). Мне это не очень нравится. Даже не из-за того, что это замедляет вызов (всё-таки основной код таких функций несоизмеримо более громоздкий), а из-за какой-то костыльности.
На ум приходит только следующее: использовать для опциональных параметров регистры или TLS. Первый вариант расходует драгоценные регистры. Второй требует сохранять ebx для адресации области памяти TLS (ну и запись в память требует больше байтов, чем простой push). Короче, варианты ещё хуже. Подкиньте идей :). Мне всё-таки хочется оставить вариант stdcall, но придумать какую-то магию чтобы передать вызов функциям C++ с меньшими усилиями 🧐
Если будут проблемы, поменяю ebp на 0x03330000 (2x = 0x06660000, 3x = 0x09990000, 5x = 0x0FFF0000), забив на фичу с [ebp+ebp*8] 😏
С одной стороны, мне не хотелось выносить всё в DLL, но потом я решил, что это даже хорошо. Почему? Об этом в следующем абзаце.
⏸ Пытаясь сварганить интру, я подумал, что было бы здорово иметь возможность написать прототип на C/C++, заодно и потестить финальную скорость. И тут я понял, что вынос основного кода в DLL — это то, что нужно. Сделать прототип можно двумя способами:
🅰️ Создав DLL с экспортируемой функцией типа ParallelixEntry, передавая такую DLL в качестве параметра при запуске среды (вместо plx-файла — да, это расширение файлов с интрами, запоминайте).
🅱️ Создав EXE, который будет вызывать функции DLL среды (в первом варианте это тоже будет, конечно, только здесь нужно будет дополнительно вызывать функцию инициализации среды, передавая параметры командной строки).
За это я ещё пока не брался, конечно же.
⏸ Сделал возможность указывать только ширину экрана с автоматическим подбором высоты, исходя из соотношения сторон текущего видеорежима. Также сделал возможность устанавливать режим экрана по умолчанию (это либо режим, установленный при запуске, либо заданный параметром командной строки). А также половину разрешения этого режима, треть и четверть по каждой из сторон. Причём, при использовании этой возможности зачастую можно сэкономить 3 байта кода.
⏸ Ещё я решил, что нужно сделать API-функцию @InitVideoOrDisplayFrame, которая будет при первом вызове устанавливать видеорежим, а при повторном — выводить изображение. Экономия на лишнем вызове (@InitVideo и @DisplayFrame тоже останутся, не переживайте).
🟰 Наверняка, я сделал что-то ещё, но не столь существенное, поэтому забыл написать об этом.
Но самый прикол в том, что файл TODO растёт быстрее, чем я успеваю что-то делать (а посвящать проекту слишком много времени я тоже не могу).
❓ Ещё один вопрос меня терзает. Это неиспользуемые параметры. Сейчас некоторые функции могут принимать переменное число параметров (2-3 или от 1 до 4-х, например). Кол-во параметров определяется отдельными битами первого. Так вот, вся проблема в том, что в конечном счёте любой API-вызов сводится к вызову функции C++, где кол-во параметров не может быть переменным (не надо про variadic — там свои заморочки). Поэтому промежуточный код (который в случае фиксированного кол-во параметров сводится к jmp) проверяет биты, записывает в стек либо реальный параметр, либо пустышку, а затем делает вызов stdcall-функции C++ (с уже фиксированным числом параметров).
В случае сложных схем (где число опциональных параметров 3 и более, либо где параметр зависит от значения нескольких битов), код получается довольно громоздким (не один десяток инструкций). Да, ещё не нужно забывать, что выходов из таких функций должно быть тоже несколько, например, ret 4, ret 8, ret 12, ret 16 :). Мне это не очень нравится. Даже не из-за того, что это замедляет вызов (всё-таки основной код таких функций несоизмеримо более громоздкий), а из-за какой-то костыльности.
На ум приходит только следующее: использовать для опциональных параметров регистры или TLS. Первый вариант расходует драгоценные регистры. Второй требует сохранять ebx для адресации области памяти TLS (ну и запись в память требует больше байтов, чем простой push). Короче, варианты ещё хуже. Подкиньте идей :). Мне всё-таки хочется оставить вариант stdcall, но придумать какую-то магию чтобы передать вызов функциям C++ с меньшими усилиями 🧐