починить р12 и sib-байт

This commit is contained in:
mzhoot 2026-06-27 13:33:19 +03:00
parent 1209ca732a
commit 3493d06200
5 changed files with 229 additions and 21 deletions

View file

@ -189,6 +189,10 @@ def encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text):
def encode_mov_reg_mem_indirect(reg_info: dict, base_reg: str) -> bytearray:
"""
Кодирует MOV reg, [base_reg] косвенная адресация через регистр.
Особые случаи x86-64:
- [rsp] и [r12] (индексы 4 и 12) требуют SIB-байта при mod=00
- [rbp] и [r13] (индексы 5 и 13) требуют disp8=0 при mod=00
"""
code = bytearray()
@ -239,16 +243,29 @@ def encode_mov_reg_mem_indirect(reg_info: dict, base_reg: str) -> bytearray:
if base_index >= 8:
rex |= 0x01 # REX.B = 1
# === 4. Формируем ModR/M байт ===
if (base_index & 7) == 5:
# [rbp] или [r13] требуют disp8=0
modrm = 0x40 | ((reg_index & 7) << 3) | 5
modrm_byte = modrm
disp8 = 0x00
else:
modrm = ((reg_index & 7) << 3) | (base_index & 7)
modrm_byte = modrm
# === 4. Формируем ModR/M и SIB байты ===
base_low = base_index & 7 # младшие 3 бита индекса
if base_low == 4:
# Особый случай: [rsp] или [r12] — требуется SIB-байт
# В x86-64 mod=00 с r/m=4 означает [SIB] без смещения
# SIB: base=4 (rsp/r12), index=4 (нет индексного регистра), scale=0
modrm_byte = 0x00 | ((reg_index & 7) << 3) | 0x04
sib_byte = 0x20 | (4 << 3) | 4 # scale=0, index=4(нет), base=4
disp8 = None
has_sib = True
elif base_low == 5:
# Особый случай: [rbp] или [r13] — требуется disp8=0
# В x86-64 mod=00 с r/m=5 означает [RIP+disp32],
# поэтому для [rbp]/[r13] используем mod=01 с disp8=0
modrm_byte = 0x40 | ((reg_index & 7) << 3) | 0x05
disp8 = 0x00
has_sib = False
else:
# Обычный случай: [reg] — mod=00, без SIB, без смещения
modrm_byte = ((reg_index & 7) << 3) | base_low
disp8 = None
has_sib = False
# === 5. Собираем инструкцию ===
@ -266,12 +283,17 @@ def encode_mov_reg_mem_indirect(reg_info: dict, base_reg: str) -> bytearray:
# 5d. ModR/M байт
code.append(modrm_byte)
# 5e. Disp8 (если есть)
# 5e. SIB байт (только для RSP/R12)
if has_sib:
code.append(sib_byte)
# 5f. Disp8 (только для RBP/R13)
if disp8 is not None:
code.append(disp8)
return code
def encode_mov_mem_reg_indirect(base_reg, reg_info):
"""
Кодирует MOV [base_reg], reg косвенная адресация.

View file

@ -21,3 +21,4 @@
1;тест_адресации_р14;Проверка косвенной адресации через R14 (ASCII код первого символа аргумента)
1;тест_адресации_р15;Проверка косвенной адресации через R15 (ASCII код первого символа аргумента)
1;двоеточие_в_строке;Проверка обработки строк с двоеточием внутри
1;тест_адресации_р12;Проверка косвенной адресации через R12 (ASCII код первого символа аргумента)
Can't render this file because it contains an unexpected character in line 2 and column 46.

View file

@ -0,0 +1,145 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тест для проверки косвенной адресации через регистр R12
Запускает скомпилированную программу с различными аргументами
и проверяет код возврата (должен быть равен ASCII коду первого символа)
"""
import subprocess
import sys
import os
from pathlib import Path
# Определяем пути
SCRIPT_DIR = Path(__file__).parent.absolute()
PROJECT_ROOT = SCRIPT_DIR.parent.parent
SOURCE_REL = SCRIPT_DIR / "тест_адресации_р12.квс"
def clean():
"""Очищает артефакты сборки"""
files = [
"тест_адресации_р12.elf",
"тест_адресации_р12.csv",
"тест_адресации_р12.аст",
"тест_адресации_р12.токены",
"тест_адресации_р12.константа",
"тест_адресации_р12_pass4.log"
]
for f in files:
f_path = SCRIPT_DIR / f
if f_path.exists():
f_path.unlink()
print(f"DEBUG: Удалён {f}", file=sys.stderr)
def build() -> bool:
"""
Собирает тестовую программу с помощью КВС
Returns:
bool: True если сборка успешна, иначе False
"""
print(f"DEBUG: Сборка {SOURCE_REL}", file=sys.stderr)
os.chdir(PROJECT_ROOT)
build = subprocess.run(
["python3", "квс_сборка.py", str(SOURCE_REL)],
capture_output=True,
text=True
)
if build.returncode != 0:
print(f"❌ Ошибка сборки: {build.returncode}", file=sys.stderr)
print(build.stderr, file=sys.stderr)
return False
print("DEBUG: Сборка успешна", file=sys.stderr)
return True
def test_with_char(char: str, expected_code: int) -> bool:
"""
Запускает скомпилированную программу с одним символом в качестве аргумента
и проверяет код возврата
Args:
char: символ для передачи в программу
expected_code: ожидаемый ASCII код символа
Returns:
bool: True если код возврата совпадает с ожидаемым, иначе False
"""
print(f"DEBUG: Тест с символом '{char}' (ожидается код {expected_code})", file=sys.stderr)
os.chdir(SCRIPT_DIR)
elf_path = SCRIPT_DIR / "тест_адресации_р12.elf"
if not elf_path.exists():
print("❌ ELF файл не найден", file=sys.stderr)
return False
# Запускаем программу с аргументом
run = subprocess.run(
[str(elf_path), char],
capture_output=True,
text=True
)
# Проверяем код возврата (должен быть ASCII код символа)
if run.returncode != expected_code:
print(f"❌ Код возврата: ожидался {expected_code}, получен {run.returncode}", file=sys.stderr)
if run.stderr:
print(f" stderr: {run.stderr}", file=sys.stderr)
if run.stdout:
print(f" stdout: {run.stdout}", file=sys.stderr)
return False
print(f"DEBUG: Тест с символом '{char}' пройден (код {run.returncode})", file=sys.stderr)
return True
def main():
"""Главная функция теста"""
print("DEBUG: Начало теста тест_адресации_р12", file=sys.stderr)
# Собираем программу
if not build():
clean()
sys.exit(1)
# Тестируем разные символы
tests = [
('A', 65), # Латинская A
('B', 66), # Латинская B
('0', 48), # Цифра 0
('z', 122), # Латинская z
('!', 33), # Восклицательный знак
('x', 120), # Латинская x
('5', 53), # Цифра 5
('_', 95), # Подчёркивание
('$', 36), # Знак доллара
('@', 64), # Собака
('#', 35), # Решётка
('*', 42), # Звёздочка
('~', 126), # Тильда
(' ', 32), # Пробел
('\t', 9), # Табуляция (может не сработать из-за экранирования)
]
all_passed = True
for char, expected_code in tests:
if not test_with_char(char, expected_code):
all_passed = False
break
# Очищаем артефакты
clean()
if all_passed:
print("Все тесты пройдены!", file=sys.stderr)
sys.exit(0)
else:
print("❌ Тест провален", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,29 @@
;./тесты/тест_адресации_р12.elf "A"
;echo $? # должен быть 65
; ============================================================
; тест_адресации_r12.квс - тест косвенной адресации через R12
; ============================================================
.текст
.глобал _start
_start:
; Получаем argv[1]
вытолкнуть рсикс ; argc
вытолкнуть рсиай ; argv[0]
вытолкнуть р12 ; argv[1] в R12
; Загружаем первый байт из argv[1] через R12
загрузить ал, [р12]
; Сохраняем в переменную (не обязательно, но для проверки)
сохранить [байт], ал
; Выход с кодом = загруженный байт
переместить_имм раикс, 60
загрузить рдиай, [байт]
вызов_системы
.данные
байт: .байт 0

View file

@ -1,18 +1,29 @@
;./тесты/тест_адресации_р11.elf "A"
;echo $? # должен быть 65
; ============================================================
; тест_адресации_r11.квс - тест косвенной адресации через R11
; ============================================================
.текст
.глобал _start
_start:
; Системный вызов write(1, msg, len)
переместить_имм раикс, 1 ; номер syscall write = 1
переместить_имм рдиай, 1 ; stdout = 1
переместить_имм рсиай, msg ; указатель на строку
переместить_имм рдикс, 21 ; длина строки
вызов_системы
; Получаем argv[1]
вытолкнуть рсикс ; argc
вытолкнуть рсиай ; argv[0]
вытолкнуть р12 ; argv[1] в R12
; Системный вызов: exit(0)
переместить_имм раикс, 60 ; номер syscall exit = 60
переместить_имм рдиай, 0 ; код возврата
; Загружаем первый байт из argv[1] через R12
загрузить ал, [р12]
; Сохраняем в переменную (не обязательно, но для проверки)
сохранить [байт], ал
; Выход с кодом = загруженный байт
переместить_имм раикс, 60
загрузить рдиай, [байт]
вызов_системы
.данные
msg: .строка "Приве : мир!\n"
байт: .байт 0