kvs/todo.txt
2026-05-02 23:04:46 +03:00

273 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

===========================
нужна сложная адресация
===========================
Хорошо, вот **полностью доработанный пошаговый план** внедрения сложной адресации во все модули компилятора КВС, с учётом:
- Поддержки `+`, `-`, `*`, `[`, `]`
- Новых мнемоник для работы с памятью
- Адресации всех типов: `[reg]`, `[disp]`, `[reg+disp]`, `[reg+reg*scale]`, `[reg+reg*scale+disp]`
- Корректной работы лексера, парсера, pass1, pass2, сборки ELF
---
# ПЛАН РЕАЛИЗАЦИИ СЛОЖНОЙ АДРЕСАЦИИ (ПОЛНАЯ ВЕРСИЯ)
## 1. Общая схема изменений по модулям
| Модуль | Что меняется | Сложность |
|-------------------|------------------------------------|-----------|
| `kvs_lexer.py` | новые токены: `[`, `]`, `+`, `-`, `*`, `NUMBER` | низкая |
| `kvs_parser.py` | новая логика операндов: `parse_operand()`, `parse_memory_operand()` | высокая |
| `kvs_data.py` | активация и уточнение форматов инструкций | средняя |
| `kvs_pass1.py` | динамический рассчёт размера инструкций с памятью | высокая |
| `kvs_pass2.py` | разбор адресации → ModR/M + SIB + disp, генерация кода | очень высокая |
---
## 2. Детальный план по шагам (в порядке реализации)
### Шаг 1 — Лексер (`kvs_lexer.py`)
**Что сделать:**
- Добавить токены:
- `LBRACKET` → `[`
- `RBRACKET` → `]`
- `PLUS` → `+`
- `MINUS` → `-`
- `STAR` → `*`
- Добавить `NUMBER` (отдельно от `WORD`) для:
- десятичных чисел (`123`)
- отрицательных (`-456`)
- шестнадцатеричных (`0x7F`)
** парсер:**
- Парсер сможет отличать `[` от начала слова
- Числа не будут смешиваться с метками/регистрами
**Результат:** новые типы токенов на выходе лексера.
---
### Шаг 2 — Парсер (`kvs_parser.py`) — основа
**Что сделать:**
2.1. Заменить прямую загрузку токена в `parse_instruction()` на вызов `parse_operand()`
2.2. Реализовать `parse_operand()`:
- Если токен `LBRACKET` → вызвать `parse_memory_operand()`
- Иначе → вернуть значение токена (регистр/число/метка/строка)
2.3. Реализовать `parse_memory_operand()`:
- Разбирает содержимое между `[` и `]`
- Поддерживает выражения вида:
- `регистр`
- `число`
- `регистр + смещение`
- `регистр + регистр * масштаб`
- `регистр + регистр * масштаб + смещение`
- любые комбинации с `+` и `-`
- Возвращает строку типа `[раикс + рсикс*8 + 16]`
**Результат:** AST будет содержать операнды памяти в каноническом строковом виде.
---
### Шаг 3 — Данные инструкций (`kvs_data.py`)
**Что сделать:**
3.1. Активировать (или добавить) записи для:
| Мнемоника | Opcode | Тип (формат) |
|-----------|--------|--------------|
| `загрузить` | `48 8B` | `mov_reg_mem` |
| `сохранить` | `48 89` | `mov_mem_reg` |
| `загрузить_байт_из_памяти` | `8A` | `mov_reg8_mem` |
| `сохранить_байт` | `88` | `mov_mem8_reg8` |
| `загрузить_адрес` | `48 8D` | `lea` |
| `переместить_сулями` | `48 0F B6` | `movzx` |
| `переместить_со_знаком` | `48 0F BE` | `movsx` |
| `втолкнуть_из_памяти` | `FF /6` | `push_mem` |
| `вытолкнуть_в_память` | `8F /0` | `pop_mem` |
| `обменять_с_памятью` | `48 87` | `xchg_mem` |
3.2. Удалить поле `type`, оставить `format` или прямо в коде проверять по мнемонике (как вы любите).
**Результат:** словарь инструкций готов для кодогенерации.
---
### Шаг 4 — Первый проход (`kvs_pass1.py`) — оценка размера
**Что сделать:**
4.1. Написать функцию `estimate_memory_operand_size(operand_str)`:
- Парсит строку `[раикс + рсикс*8 + 16]`
- Определяет:
- наличие SIB (нужен, если есть индексный регистр)
- размер смещения (disp8 = 1 байт, disp32 = 4 байта)
- Возвращает дополнительные байты: 0, 1, 4 или 5 (SIB + disp)
4.2. Модифицировать `estimate_size()`:
- Для инструкций с памятью (список из шага 3) считать:
- базовый размер opcode (REX + опкод)
- +1 байт (ModR/M)
- +1 байт (SIB, если нужен)
- + размер смещения (1 или 4)
**Результат:** правильное вычисление `.text` и адресов меток.
---
### Шаг 5 — Второй проход (`kvs_pass2.py`) — ядро адресации
Это самый объёмный шаг. Реализовать **поэтапно**:
#### 5.1. Структура данных для режима адресации
Создать класс/словарь `AddressingMode`:
- `has_base` (bool)
- `base_reg` (int 015 или None)
- `has_index` (bool)
- `index_reg` (int 015)
- `scale` (1,2,4,8)
- `has_disp` (bool)
- `disp_value` (int)
- `disp_size` (1 или 4)
#### 5.2. Парсер адресации
Написать `parse_addressing_mode(operand_str)`:
- Вход: `[раикс + рсикс*8 + 16]`
- Выход: `AddressingMode`
- Поддержка всех форм из шага 2.3
#### 5.3. Кодирование ModR/M
Написать `encode_modrm_reg_mem(reg, addr_mode)`:
- Формирует байт ModR/M:
- `mod` (00, 01, 10, 11)
- `reg` (3 бита — номер регистра)
- `r/m` (3 бита — зависит от наличия SIB)
#### 5.4. Кодирование SIB
Написать `encode_sib(addr_mode)`:
- Формирует байт SIB:
- `scale` (2 бита)
- `index` (3 бита)
- `base` (3 бита)
#### 5.5. Кодирование смещения
Написать `encode_displacement(addr_mode, current_pos, vaddr_text)`:
- Для `disp8` → 1 байт со знаком
- Для `disp32` → 4 байта (little-endian)
#### 5.6. Генераторы инструкций
Написать для каждой мнемоники:
| Функция | Для какой инструкции |
|---------|----------------------|
| `encode_mov_reg_mem` | `загрузить` |
| `encode_mov_mem_reg` | `сохранить` |
| `encode_mov_reg8_mem` | `загрузить_байт_из_памяти` |
| `encode_mov_mem8_reg8` | `сохранить_байт` |
| `encode_lea` | `загрузить_адрес` |
| `encode_movzx` | `переместить_сулями` |
| `encode_movsx` | `переместить_со_знаком` |
| `encode_push_mem` | `втолкнуть_из_памяти` |
| `encode_pop_mem` | `вытолкнуть_в_память` |
| `encode_xchg_mem` | `обменять_с_памятью` |
Каждая функция:
- Берёт операнды (регистр + операнд памяти)
- Вызывает `parse_addressing_mode` для операнда памяти
- Формирует REX-префикс (W, R, B, X биты)
- Добавляет opcode
- Добавляет ModR/M
- Добавляет SIB (если нужен)
- Добавляет disp
#### 5.7. Диспетчер
Модифицировать `encode_instruction()`:
- Добавить ветки для всех новых мнемоник
- Вызывать соответствующие функции из 5.6
---
### Шаг 6 — Тестирование (итеративное)
Тестировать после каждого подшага 5.x:
| Тест | Что проверяет |
|------|---------------|
| `загрузить раикс, [рбикс]` | ModR/M, нет SIB, нет disp |
| `загрузить раикс, [1234]` | disp32, r/m=101 |
| `загрузить раикс, [рбикс + 8]` | disp8 |
| `загрузить раикс, [рбикс + 1000]` | disp32 |
| `загрузить раикс, [рбикс + рсикс]` | SIB, нет disp |
| `загрузить раикс, [рбикс + рсикс*8 + 16]` | SIB + disp8 |
| `сохранить [рдиай], рсикс` | MOV mem, reg |
| `загрузить_адрес р11, [рбикс + рсикс*4]` | LEA |
---
### Шаг 7 — Стандартная библиотека (параллельно или после)
После того как адресация заработает, можно добавить:
| Файл | Содержание |
|------|------------|
| `libkvs.квс` | `malloc`, `free`, `strlen`, `strcmp`, `memcpy`, `putchar`, `getchar`, `printf` (упрощённый) |
| `libkvs_data.квс` | статические данные для библиотеки |
Библиотека будет использовать сложную адресацию для работы со структурами и кучей.
---
## 3. Зависимости между подзадачами
```
Лексер (шаг 1)
Парсер (шаг 2)
Данные инструкций (шаг 3)
Pass1 (шаг 4) ← может тестироваться отдельно
Pass2:
5.1 → 5.2 → 5.3 → 5.4 → 5.5
5.6 (для каждой мнемоники)
5.7 (диспетчер)
Тестирование (шаг 6)
Библиотека (шаг 7)
```
---
## 4. Итог: что получим на выходе
- **Лексер** выдаёт `LBRACKET`, `RBRACKET`, `PLUS`, `MINUS`, `STAR`, `NUMBER`
- **Парсер** понимает `[раикс + рсикс*8 - 16]`
- **Pass1** правильно считает размер инструкций с памятью
- **Pass2** генерирует правильный машинный код для всех форм адресации
- **ELF** содержит корректные инструкции x86-64 с ModR/M, SIB, disp
- **Библиотека** позволяет писать более сложные программы (malloc, строки)
---
---
План готов. Когда скажете — начинаем писать код по шагам.