Programming WHDLoad

### Схематичний опис роботи програми

Наступна таблиця показує процес виконання програми, коли запускається програма, установлена за допомогою WHDLoad. Я сподіваюсь, що це допоможе зрозуміти, як працює WHDLoad і як WHDLoad, slave-модуль і встановлена програма взаємодіють між собою.

КОРИСТУВАЧ

- запускає демо або гру, клацаючи на іконку або запускаючи WHDLoad через командний рядок

Операційна Система

- завантажує WHDLoad і виконує запуск

WHDLoad

- перевіряє програмне середовище й апаратні засоби
- завантажує й перевіряє slave-модуль
- резервує необхідну кількість пам'яті для установленої програми
- якщо активізовано опцію Preload/S, те в оперативну пам'ять завантажуються образи диска і файли (наскільки дозволяє вільна пам'ять)
- відключає OS (відключає багатозадачність й преривання, перемикає графіку на режим OCS, ініціалізує всі апаратні засоби, використовуючи задані значення)
- робить перехід на виконання slave-модуля

Slave-модуль

- завантажує головний виконуваний файл установленої програми, викликаючи функцію WHDLoad (наприклад, resload_DiskLoad або resload_LoadFile)
- виправляє виконуваний файл (щоб програма завантажила свої дані через slave, усуває проблеми несумісності, активізує функцію виходу із програми)
- запускає головний виконуваний файл, що,

Установлена програма

- виконується
- при завантаженні даних з диска, викликає slave-модуль (тому що slave-модуль виправив файл, що виконується,), slave-модуль викликає WHDLoad, і WHDLoad частково активізує OS щоб завантажити дані (тільки якщо дані не були попередньо завантаженні - див. Preload), потім повертається у встановлену програму й програма продовжує виконання

КОРИСТУВАЧ

- виходить із програми, натискаючи клавішу виходу QuitKey

Slave-модуль

- повертається в WHDLoad, викликаючи resload_Abort

WHDLoad

- перезапускає OS (відновлює стан регістрів, пам'яті, дисплея)
- звільняє всі зайняті ресурси
- робить повернення в OS

### Як встановити диск, який використовує трековий завантажник (trackloader)

Це зовсім короткий покроковий опис того, як створити патч, що використає WHDLoad. Даний пункт пояснює найпростіший варіант. У реальності такого варіанта швидше за все не буде. У наступних главах, будуть окремо описані особливі випадки й проблеми, з якими ви можете зустрітися.

1. Попередня підготовка
   - Створіть папку, у якій будуть перебувати всі файли.
   - Створіть у цій же папці образ диска, використовуючи DIC.
   - Створіть *? .info файл, у якому інструментом за замовчуванням є "WHDLoad" і параметром (Tooltype) "SLAVE=#?", де "#?" - ім'я slave-модуля (або просто скопіюйте іконку з інсталяційного приклада й відключіть всі параметри (Tooltypes) крім "SLAVE=").
2. Slave-модуль
   Для створення slave-модуля нам необхідна наступна інформація:
   1) Де на диску розташований головний виконуючий файл?
   2) Де усередині головного виконуючого файлу розташовується завантажник диска?
   Щоб одержати цю інформацію ми спочатку аналізуємо завантажувальний блок (bootblock). Як правило, головний виконуючий модуль, що, завантажується звідси, використовуючи функцію exec.DoIO(). Іноді в завантажувальному блоці присутній завантажник доріжки (trackloader). Тепер ми напишемо Slave-модуль, який буде емулювати завантажувальний блок й завантажувати головний виконуючий модуль і образи диска. Зараз нам необхідно витягти головний виконуючий модуль з образа диска або з дампа пам'яті. Після цього нам потрібно знайти завантажник у головному виконуючому файлі. Найшвидший шлях полягає в тому, щоб спробувати знайти у HEX-редакторі послідовність `$AAAAAAAA` (використовується MFM-декодуванням). Виріжте знайдену область із захватом +/- $1000 байт, дизасемблюйте, і шукайте початок процедури. Коли завантажник буде знайдений, розберіться які параметри завантаження він використовує (адреса завантаження, довжина). Тепер ми створюємо код для slave-модуля, який виправить цю процедуру завантажника, і який буде обробляти всі запити переадресовуючи їх slave-модулю. Slave-модуль буде коректувати параметри завантаження й викликати функцію WHDLoad'а resload_DiskLoad.
3. У випадку такого ідеального варіанта, slave-модуль уже готовий.
   Тепер залишилося єдине - створити гарну іконку. Сграбте дві картинки, використовуючи опцію Snoop програми WHDLoad й SP або системний монітор, або UAE, і створіть іконку. Рекомендується використовувати 16 кольорову RomIcon палітру.

### Можливі проблеми й особливі випадки

#### Нестандартний завантажник доріжки

Деякі програми використовують свій власний формат диска. Це означає, що DIC не в змозі створити образ такого диска. Щоб прочитати файли з такого диска або створити його образ, рекомендується використати RawDIC. За подробицями звертайтеся до документації програми RawDIC.

#### Програма використовує кілька дисків

Якщо програма використовує, то більше чим один диск, те slave-модуль повинен переадресувати доступи до дисків до відповідного файлу образа. Іноді це не легко. Деякі програми підтримують більше одного дисководу, так що ви можете використати номер дисководу для вибору потрібного диска. Більшість програм використовує ідентифікатор (наприклад назва) на кожному диску, щоб розрізняти їх. У цьому випадку, використайте змінну, котра містить номер диска, і при кожному зверненні до ідентифікатора диска (визначайте такий доступ, аналізуючи параметри для завантажника диска) збільшуйте змінну (а якщо досягнуто останнього диска, то зменшуйте її), у надії на те, що завантажник буде перечитувати ідентифікатори знову й знову поки не виявить потрібний диск. Деякі програми видають запит, щоб користувач вставив правильний диск - відключіть цей запит.

#### Збереження таблиці рекордів (Highscore)

Ну, що тут казати. Використовуйте функцію resload_SaveFile, щоб записати відповідну область пам'яті на диск. Якщо хочете, то можете ще й зашифрувати її так, щоб "чайникам" було не легко її виправити. Не рекомендується робити запис безпосередньо в образ диска (використовуючи функцію resload_SaveFileOffset) тому, що якщо щось піде не так, як треба (наприклад, яка-небудь помилка або зависання), то можливо, що образ диска буде ушкоджений.

#### Збереження прогресу гри (Savegames)

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

#### Доступи до операційної системи

Під час роботи slave-модуль і встановлена програма не повинні мати ніяких звернень до OS! Тому всі звернення з установленої програми повинні бути відключені або переспрямовані. Якщо їх не так багато й вони не потрібні при роботі через WHDLoad (типу exec.Disable() або exec.SuperState()), то просто можна змінити їх командою NOP (`$4e71`). Якщо виклики несуть важливу функцію (типу exec.DoIO()), переадресуйте їх slave-модулю, і емулюйте їх. Також можете створити просту exec.library в невикористаній області пам'яті (проініціалізируйте довге слово за адресою `$4`). Подивитеся як це зроблено у вихідному тексті для модуля "Oscar.slave", що емулює exec.AllocMem(). Для виявлення таких викликів OS, виставте початковий execbase в `$f0000001` з тією метою, щоб всі програми, які можуть використовувати execbase, видали "Помилку Адреси".
Якщо емулювати функції OS занадто складно, тоді просто використайте один з kickemu-пакетів, які можуть бути знайдені в дистрибутиві whdload-dev. Там є один пакет для Kick 1.3 ('src/sources/whdload/kick13.s'') і один для Kick 3.1 ('src/sources/whdload/kick31.s''). Ці пакети вимагають оригінального образа кікстарта й створюють повноцінну OS в WHDLoad. Див. також файли readme, які поставляються в комплекті із цими пакетами

### Проблеми сумісності

#### Обмежений адресний простір на 68000/68010/68ec020

На цих процесорах адресний простір обмежено розміром 16 Мб (`$000000... $ffffff`), тому що центральний процесор має тільки 24 адресні лінії. У результаті всі доступи до більш високих адрес виконуються до адрес нижче 16 Мб, просто ігноруючи адреси старші 8 біт. Деякі програми використовують ці біти для зберігання даних або просто забувають очищати їх. На процесорах з повним 4Гб адресним простором, типу 68020/680ec30/68030/68040/68060 це не відбуватиметься, тому що звернення буде йти до повної 32-х бітної адреси.
Для рішення цієї проблеми, ви повинні підкоректувати ці виклики й перенаправляти їх на відповідні адреси.
Іноді причиною доступу до дивних адрес може бути непроініціалізований покажчик. У цьому випадку може допомогти очищення `$400` - ws_BaseMemSize.

#### Різний стековий фрейм на різних процесорах

Стекові фрейми, створені процесором після преривання і подій, різні для різних процесорів класу 68ДО. На 68000 стековий фрейм становить 6 байт, за винятком "Помилки Адреси" і "Помилки Шини". У стековому фреймі перебуває збережене значення SR в (a7) і PC в (2,a7). На всіх інших процесорах (68010 +) мінімальний стековий фрейм становить 8 байт і додатково містить номер вектора як слово в (6,a7). Цей формат стекового фрейму $0, що складається з чотирьох слів, створений для "Trap #xx" і переривань на 68010-68060. Стекові фрейми для інших подій різні на кожному процесорі. Інструкція RTE на 68000 працює інакше чим на 68010+. На 68000 вона просто відновлює SR й PC і продовжує виконання програми з перерваної адреси. На 68010 + вона додатково звільняє стековий фрейм в залежності від його формату.
Деякі програми проштовхують (записують у стік?) адресу (PC) і SR, а потім виконують інструкцію RTE. Це працює тільки на процесорах 68000, а на 68010+ результат непередбачений.
Якщо програма працює таким чином, ви повинні виправити це місце. Іноді буває досить замінити RTE на RTR.

#### MOVEM.x RL,-(An) на 68000/010 й 68020/030/040

Є відмінність, якщо регістр, що використовується у випадку адресації з попереднім зменшенням адреси, то виконання команди (RL) також утримується у списку регістрів. На процесорах 68020, 68030, 68040 значення, записане у пам`ять є вихідним значенням регістра, зменшеним на розмір операції. На процесорах же 68000 й 68010 записується вихідне значення (не зменшене).
Оскільки ця інструкція рідко використовується, то на даний момент не помічено ні однієї програми, яка б мала проблеми через описані розходження.

### Загальні принципи написання патчей

- Не змінюйте стан регістрів (таких як VBR або CACR), присутніх на старших моделях процесорів. З погляду людини, яка програмує Slave-модуль, VBR завжди дорівнює 0, навіть якщо він був переміщений, тому що емулюються автовектори (а також Traps при умові, що встановлено прапорець WHDL_EmulTrap). Біти в CACR різні для кожного процесора. Є тільки один надійний спосіб змінювати Cache - використати resload_SetCACR/SetCPU і bitdef'и з 'exec/execbase.i' й 'whdload.i'. Треба також забрати всі звертання до цих регістрів з установленої програми, інакше WHDLoad не буде правильно працювати!
- Ніколи не змінюйте образи дисків. Це корисно у випадку, якщо хтось захоче запустити програму з дискети, то йому потрібно буде просто записати образ знову на диск (за умови, що програма працює без використання яких-небудь патчей, хоча, не можна сказати що таке буває в більшості випадків).
- Ніколи не використовуйте оригінальний код з програми безпосередньо в slave-модулі (з погляду захисту авторських прав).
- Включайте кеші тільки якщо ви впевнені, що це запрацює на всіх процесорах.
- Наскільки це можливо, використовуйте як можна менше пам'яті для ws_BaseMemSize. У деяких людей наприкінці Chip-пам'яті висять резидентні програми, так що це допоможе використати тільки `$1f0000`, замість $`200000` й WHDLoad зможе використати всю зарезервовану пам'ять.

### Поради й маленькі хитрощі

#### Що краще використовувати: образи дисків або файли?

Іноді перед вами буде стояти вибір, що використовувати, образи дисків або файли. Обидва способи мають свої переваги. Використання образів диска, як правило, більше легкий і більше швидкий спосіб створити slave-модуль. Але реальні файли значно простіше кешувати (якщо дуже мало пам'яті або пам'ять фрагментирована). Зайнятого місця на жорсткому диску також буде менше при роботі з реальними файлами в порівнянні з використанням образів дисків. Рекомендується використати образи дисків тільки в тому випадку, якщо файлів дуже багато (більше 30-ти).