Документація користувача EnotVM32 від https://chat.deepseek.com/ 1. Загальний опис EnotVM32 — це проста 32?бітна віртуальна машина (емулятор), написана на Turbo Pascal 4.0. Вона дозволяє виконувати програми, скомпільовані у власний бінарний формат (`BOOT.bin`). Проєкт відкритий, поширюється під ліцензією MIT. Основні цілі: - мінімальний набір команд, легкий для перенесення на інші платформи; - схожість на асемблер x86 (спрощене перенесення коду); - використання макросів FASM для написання програм; - відкритий вихідний код емулятора. 2. Встановлення та перший запуск 1. Розпакуйте архів `EnotVM32` у теку `C:\EnotVM32\BP4_001\`. У ній вже знаходяться: - `EnotVM32.EXE` – готова програма; - файли `*.BGI` для графіки (не обов’язкові для текстових програм); - папка `Examples\` з прикладами. 2. Виберіть приклад (наприклад, `Examples\hello\BOOT.bin`) та скопіюйте його в `BP4_001\`. 3. Запустіть `EnotVM32.EXE` — він прочитає `BOOT.bin` і почне виконання. 3. Розробка власних програм (за допомогою FASM) Для написання програм потрібен flat assembler (FASM) версії 1.73 або інші версії. 3.1. Встановлення FASM - Завантажте з офіційного сайту (https://flatassembler.net/download.php): - `fasm17335.zip` (для DOS) або `fasmw17335.zip` (для Windows). - Розпакуйте, наприклад, у `C:\FASMdos\` або `C:\FASMwin\`. 3.2. Структура проєкту У папці з прикладами ви знайдете: - `Macros.asm` – набір макросів, що визначають імена регістрів та мнемоніки команд EnotVM32 (обов’язковий файл). - `BOOT.asm` – вихідний текст програми. - `BOOT.bin` – скомпільований образ (оновлюється після компіляції). 3.3. Компіляція Варіант А – з інтегрованим редактором (DOS / Windows) Використовуйте готові bat?файли (є в комплекті прикладів): - `EditDos.bat` – редагування та компіляція через DOS?версію FASM. - `EditWin.bat` – те саме для Windows. У редакторі натисніть `Ctrl+F9` – буде створено `BOOT.bin`. Варіант Б – ручна компіляція - `ComplDos.bat` або `ComplWin.bat` автоматично викличуть FASM. - Увага: ці bat-файли спочатку видаляють старий `BOOT.bin`. Якщо це не потрібно, закоментуйте рядок `del BOOT.bin` (додайте `REM` на початку). Після компіляції скопіюйте `BOOT.bin` у папку з `EnotVM32.EXE` та запустіть емулятор. 4. Архітектура EnotVM32 4.1. Регістри - 256 32-бітних регістрів (масив `RegsDW[0..255]`). - Довгі назви: від `AA` до `JV` (деякі зарезервовані FASM мають суфікс `_`, наприклад `AH_`, `AL_`). - Короткі назви: від `A` до `Z` (відповідають `AA` – `AZ`). - Спеціальні регістри: - `CMP1` (254), `CMP2` (255) – для операцій порівняння. - `YA..YH` (232–239) – вхідні параметри для системних викликів `EXTR`. - `ZA..ZH` (240–247) – вихідні значення після `EXTR`. - Регістри `RgEIP` (вказівник інструкцій) та `RgESP` (вказівник стеку) безпосередньо не доступні через `MOV`, але будуть доступні через майбутні розширення `EXTR`. 4.2. Пам’ять - Основна пам’ять: масив байтів `RAM[0..cSizeOfRAM]`, за замовчуванням 48 КБ (49152 байти, адреси 0..49151). Розмір можна змінити в `uTpDt.pas` (константа `cSizeOfRAM`). - Розширена пам’ять: до 11 сторінок (`cMaxPagesOfRAMext = 10`) по 48 КБ кожна. Сторінки створюються динамічно під час ініціалізації. Доступ через спеціальні `EXTR`-команди копіювання. 4.3. Стек - Окремий масив `RAMc[0..cSizeOfRAMstack] of Int32` (за замовчуванням 256 елементів). - `RgESP` адресує елементи Int32, а не байти. Ініціалізується значенням `cSizeOfRAMstack` (вершина стеку). - Команди `PUSH`/`POP` змінюють `RgESP` на 1. 4.4. Типи даних - Усі регістри — знакові 32-бітні цілі (діапазон –2147483648 … 2147483647). - Арифметичні операції (`ADD`, `SUB`, `MUL`, `DIV`, `MOD`) виконуються як знакові. - Логічні операції (`AND`, `OR`, `XOR`, `NOT`, `SHL`, `SHR`) — побітові. - Зсуви `SHL`/`SHR` працюють з 32?бітним операндом; кількість зсувів береться з регістра. 5. Система команд Повний перелік команд (опкод, мнемоніка, довжина, опис) наведено у файлі `02 Команди EnotVM32.txt`. Нижче – стислий довідник. 5.1. NOP `00` – `nop_` (1 байт) – нічого не робить. 5.2. Команди пересилання (MOV) Усі варіанти доступу до пам’яті: безпосередня адреса (`[imm32]`) або через регістр (`[Rg]`), а також розмір даних: 32 біти (MOV), 16 біт (MOV2), 8 біт (MOV1). 32?бітні (регістр < значення/пам’ять): - `01` MOVrv Rg, imm32 - `02` MOVrr Rg1, Rg2 - `03` MOVrm Rg, [imm32] - `04` MOVrmr Rg1, Rg2 (Rg1 := [Rg2]) - `05` MOVmr [imm32], Rg - `06` MOVmrr Rg1, Rg2 ([Rg1] := Rg2) 16?бітні (читання з розширенням до 32 біт): - `07` MOV2rm Rg, [imm32] - `2B` MOV2rmr Rg1, Rg2 - `08` MOV2mr [imm32], Rg (записуються молодші 16 біт) - `2C` MOV2mrr Rg1, Rg2 8?бітні (читання з розширенням до 32 біт): - `09` MOV1rm Rg, [imm32] - `2D` MOV1rmr Rg1, Rg2 - `0A` MOV1mr [imm32], Rg - `2E` MOV1mrr Rg1, Rg2 5.3. Переходи та виклики - `0B` CALLa imm16 – виклик підпрограми за абсолютною адресою. - `0C` CALLr Rg – виклик за адресою в регістрі. - `0D` RET – повернення. - `0E` GOTOa imm16 – безумовний перехід. - `0F` GOTOr Rg – безумовний перехід через регістр. 5.4. Умовні переходи (знакові) Порівнюються регістри `CMP1` і `CMP2`. Їх можна завантажити командою `CMPrr` (`29`) або будь?яким `MOV`. - `10` GOTOsaIs imm16 – якщо CMP1 = CMP2 - `11` GOTOsaNotIs imm16 – якщо CMP1 ? CMP2 - `12` GOTOsaMoreIs imm16 – якщо CMP1 ? CMP2 - `13` GOTOsaLess imm16 – якщо CMP1 < CMP2 - `14`-`17` – ті самі умови, але адреса переходу в регістрі (GOTOsr...). 5.5. Стекові команди - `18` PUSHr Rg – покласти регістр у стек. - `19` POPr Rg – витягти зі стеку. - `1A` PUSHrsvr Rg1, Rg2 – зберегти регістри з діапазону Rg1..Rg2. - `1B` POPrsvr Rg1, Rg2 – відновити діапазон. 5.6. Арифметичні та логічні операції (регістр–регістр) `1C` ADD, `1D` SUB, `1E` MUL, `1F` DIV, `20` MOD, `21` INC, `22` DEC, `23` AND, `24` OR, `25` XOR, `26` NOT, `27` SHL, `28` SHR. Усі, крім `INC`, `DEC`, `NOT`, мають довжину 3 байти (`opcode + Rg1 + Rg2`). `INC`, `DEC`, `NOT` – 2 байти. 5.7. Порівняння `29` CMPrr Rg1, Rg2 – записує Rg1 у CMP1, Rg2 у CMP2. 5.8. Системний виклик EXTR `2A` EXTR word_value – виклик функцій розширення (аналог переривань). Детальний опис функцій – у наступному розділі. 6. Системні виклики EXTR Команда `EXTR` (опкод `2Ah`) приймає 16?бітний параметр, який задає групу та номер функції. Деякі функції використовують регістри `YA..YH` для вхідних даних, а результати повертають у `ZA..ZH`. Нижче – перелік реалізованих функцій. 6.1. Група 0 (загальні) | Номер | Назва | Вхідні | Вихідні | Опис | |-------|-------------------------------------|--------|---------|------| | 0 | EXTR_NOP | – | – | Порожня операція (для тестів) | | 1 | CheckEXTR | YA = номер команди | ZA=1 існує, ZA=0 не існує | Перевірка наявності EXTR?команди | | 2 | HALT_ | YA = код виходу | – | Негайне завершення програми (Halt) | | 3 | Writeln_GetTimeStr | – | – | Вивести поточний час (год:хв:сек.соті) | | 4 | Readln0 | – | – | Очікування натискання Enter | | 5 | VersionEnotVM | – | ZA = версія | Версія EnotVM (константа cVersionEnotVM) | | 6 | Writeln0 | – | – | Вивести порожній рядок | | 7 | CONT | – | – | Запит “Will Enter 'cont' or 'CONT' for continue” і очікування вводу | | 8 | WritelnYA | – | – | Вивести значення YA у десятковому та шістнадцятковому вигляді | | 9 | Wait00secTime_and_Writeln_GetTimeStr| – | – | Чекати, поки лічильник секунд стане 0, потім вивести час | | 10 | EndProgram | – | – | Коректне завершення роботи двигуна | | 11 | TypeOfAmbience | – | ZA = 1 (16-біт) або 2 (32-біт) | Тип середовища | | 12 | APM | – | – | Обробка повідомлень (сумісність), встановлює pSTOPEngine=3 | | 13 | SizeOfRAMstack | – | ZA = розмір стеку (в елементах) | | | 14 | SizeOfRAM | – | ZA = cSizeOfRAM | Розмір основної пам’яті | | 15 | SizeOfRAMext | – | ZA = cSizeOfRAMext | Розмір сторінки розширеної пам’яті | | 16 | MaxPagesOfRAMext | – | ZA = cMaxPagesOfRAMext | Кількість сторінок (від 0 до …) | | 17 | (середовище компіляції) | – | ZA = cEnotVMItIsCompiled | Тип компілятора (1 = TP 4.0) | 6.2. Група 1 – рядковий ввід/вивід (256–511) | Номер | Назва | Вхідні | Вихідні | Опис | |-------|--------------|--------|---------|------| | 256 | WritelnStr | YA = адреса рядка у спеціальному форматі | – | Вивести рядок з переводом рядка | | 257 | WriteStr | YA = адреса рядка | – | Вивести рядок без переводу рядка | Рядки в пам’яті зберігаються у форматі: - 1 байт – тип (`1`…`4`), - далі поля довжини та поточної довжини, - потім власне символи. 6.3. Група 2 – копіювання пам’яті (512–767) Усі функції використовують регістри: - `YB` – кількість байт (Count) - `YC`, `YD`, `YE`, `YF` – адреси/номери сторінок (див. нижче) | Номер | Назва | Параметри | |-------|-----------------|-------------------------------------------------------------------| | 512 | CopyMainToMain | YC = Src, YB = Count, YE = Dst | | 513 | CopyMainToExt | YC = MainSrc, YB = Count, YF = PageDst, YE = ExtDst | | 514 | CopyExtToMain | YD = PageSrc, YC = ExtSrc, YB = Count, YE = MainDst | | 515 | CopyExtToExt | YD = PageSrc, YC = ExtSrc, YB = Count, YF = PageDst, YE = ExtDst | Примітка: перекриття ділянок при копіюванні обробляється коректно (використовується процедура `Move`). 6.4. Група 3 – беззнакові умовні переходи (768–1023) Ці команди порівнюють пару `CMP1`/`CMP2` як беззнакові 32?бітні числа (старше слово, потім молодше). | Номер | Назва | Дія | |-------|----------------|---------------------------------------------------------------| | 768 | GOTOuaMoreIs | Якщо CMP1 ? CMP2 (беззнаково) — перехід за 16?бітною адресою | | 769 | GOTOuaLess | Якщо CMP1 < CMP2 — перехід | | 770 | GOTOurMoreIs | Те саме, але адреса у регістрі, вказаному після EXTR | | 771 | GOTOurLess | Аналогічно | 7. Приклад найпростішої програми Файл `BOOT.asm` (з використанням макросів `Macros.asm`): asm format binary as 'bin' include 'Macros.asm' ; Вивести "Hello, World!" та завершити MOVrv A, hello_string ; YA := адреса рядка EXTR 256 ; WritelnStr EXTR 10 ; EndProgram hello_string: db 1, 13, 13 ; тип 1, повна довжина=13, поточна=13 db 'Hello, World!' Після компіляції отриманий `BOOT.bin` скопіюйте до `EnotVM32.EXE` і запустіть. 8. Ліцензія Проєкт поширюється під "MIT License". Повний текст ліцензії додається окремим файлом `LICENSE`. Ви можете вільно використовувати, змінювати та розповсюджувати як вихідний код, так і скомпільовані програми. --- Документація підготовлена на основі оригінальних описів та вихідного коду.