ремонт регистров р9 - р11

This commit is contained in:
mzhoot 2026-06-20 10:34:59 +03:00
parent 50d9a5b7be
commit ce1800f236
10 changed files with 591 additions and 32 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
__pycache__/
.gigacode
*.pyc
*.pyo

View file

@ -186,44 +186,92 @@ def encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text):
return code
def encode_mov_reg_mem_indirect(reg_info, base_reg):
def encode_mov_reg_mem_indirect(reg_info: dict, base_reg: str) -> bytearray:
"""
Кодирует MOV reg, [base_reg] косвенная адресация.
Пример: mov rax, [rbx] 48 8B 03
Аргументы:
- reg_info: словарь от get_reg_info() с ключами 'index' (int) и 'size' (int)
- base_reg: строка с именем регистра, например 'рбикс'
REGISTERS[base_reg] возвращает число-индекс (int), НЕ словарь.
Кодирует MOV reg, [base_reg] косвенная адресация через регистр.
"""
code = bytearray()
reg = reg_info['index']
base = REGISTERS[base_reg] # REGISTERS возвращает int (индекс регистра)
rex = 0x48 if reg_info['size'] == 64 else 0x40
if reg >= 8:
rex |= 0x04 # REX.R
if base >= 8:
rex |= 0x01 # REX.B
code.append(rex)
code.append(0x8B) # MOV r64, r/m64
# === 1. Получаем индексы регистров ===
reg_index = reg_info['index']
reg_size = reg_info['size']
# Особый случай: [rbp] или [r13] требует disp8=0 при mod=00
# (mod=00 с r/m=101 означает RIP-relative, а не [rbp])
if (base & 7) == 5:
# mod=01 (disp8), reg, r/m=5
modrm = 0x40 | ((reg & 7) << 3) | 5
code.append(modrm)
code.append(0x00) # disp8 = 0
if base_reg not in REGISTERS:
raise ValueError(f"Неизвестный базовый регистр: {base_reg}")
base_index = REGISTERS[base_reg]
# === 2. Выбираем opcode и префиксы ===
#
# В x86-64 для MOV существуют разные opcodes:
# - 8A /r — MOV r8, r/m8 (только 8-бит!)
# - 8B /r — MOV r16/r32/r64, r/m16/r/m32/r/m64
#
# Для 16-бит нужен префикс 0x66
# Для 64-бит нужен REX.W (бит 3 в REX)
#
if reg_size == 8:
opcode = 0x8A # MOV r8, r/m8
rex_base = 0x40 # Без REX.W
need_prefix_66 = False
elif reg_size == 16:
opcode = 0x8B # MOV r16, r/m16
rex_base = 0x40 # Без REX.W
need_prefix_66 = True # ← ВАЖНО: префикс 0x66 для 16-бит!
elif reg_size == 32:
opcode = 0x8B # MOV r32, r/m32
rex_base = 0x40 # Без REX.W
need_prefix_66 = False
elif reg_size == 64:
opcode = 0x8B # MOV r64, r/m64
rex_base = 0x48 # С REX.W (бит 3 = 1)
need_prefix_66 = False
else:
# mod=00, reg, r/m=base
modrm = ((reg & 7) << 3) | (base & 7)
code.append(modrm)
raise ValueError(f"Неподдерживаемый размер регистра: {reg_size} бит")
# === 3. Формируем REX-префикс ===
rex = rex_base
# REX.R: для регистра-источника (reg) — нужен если индекс >= 8
if reg_index >= 8:
rex |= 0x04 # REX.R = 1
# REX.B: для базового регистра (base) — нужен если индекс >= 8
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
disp8 = None
# === 5. Собираем инструкцию ===
# 5a. Префикс 0x66 для 16-битных операндов (ДО REX!)
if need_prefix_66:
code.append(0x66)
# 5b. REX-префикс (если не равен 0x40)
if rex != 0x40:
code.append(rex)
# 5c. Opcode
code.append(opcode)
# 5d. ModR/M байт
code.append(modrm_byte)
# 5e. Disp8 (если есть)
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

@ -13,4 +13,7 @@
1;тест_бнд_3;Проверка BSS: смешанная адресация (абсолютная запись + косвенное чтение)
1;сложение;Тест сложения 40+2=42 (проверка через код возврата)
0;сложение+;Тест сложения 40+2=42 (проверка через код возврата+)
1;вычитание;Тест вычитания 50-8=42
1;вычитание;Тест вычитания 50-8=42
1;тест_адресации_р9;Проверка косвенной адресации через R9 (ASCII код первого символа аргумента)
1;тест_адресации_р10;Проверка косвенной адресации через R10 (ASCII код первого символа аргумента)
1;тест_адресации_р11;Проверка косвенной адресации через R11 (ASCII код первого символа аргумента)
Can't render this file because it contains an unexpected character in line 2 and column 46.

View file

@ -5,7 +5,7 @@ import csv
from pathlib import Path
def main():
# Диспетчер лежит в тесты/kvs_full_test.py
# Диспетчер лежит в тесты/прогон_тестов.py
# Значит, корень с табличкой — та же папка (тесты/)
tests_dir = Path(__file__).parent
csv_file = tests_dir / "kvs_full_test.csv"

View file

@ -0,0 +1,140 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тест для проверки косвенной адресации через регистр R10
Запускает скомпилированную программу с различными аргументами
и проверяет код возврата (должен быть равен 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 / "тест_адресации_р10.квс"
def clean():
"""Очищает артефакты сборки"""
files = [
"тест_адресации_р10.elf",
"тест_адресации_р10.csv",
"тест_адресации_р10.аст",
"тест_адресации_р10.токены",
"тест_адресации_р10.константа",
"тест_адресации_р10_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 / "тест_адресации_р10.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: Начало теста тест_адресации_р10", 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), # Собака
]
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 @@
;./тесты/тест_адресации_р10.elf "A"
;echo $? # должен быть 65
; ============================================================
; тест_адресации_r10.квс - тест косвенной адресации через R10
; ============================================================
.текст
.глобал _start
_start:
; Получаем argv[1]
вытолкнуть рсикс ; argc
вытолкнуть рсиай ; argv[0]
вытолкнуть р10 ; argv[1] в R10
; Загружаем первый байт из argv[1] через R10
загрузить ал, [р10]
; Сохраняем в переменную (не обязательно, но для проверки)
сохранить [байт], ал
; Выход с кодом = загруженный байт
переместить_имм раикс, 60
загрузить рдиай, [байт]
вызов_системы
.данные
байт: .байт 0

View file

@ -0,0 +1,142 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тест для проверки косвенной адресации через регистр R11
Запускает скомпилированную программу с различными аргументами
и проверяет код возврата (должен быть равен 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 / "тест_адресации_р11.квс"
def clean():
"""Очищает артефакты сборки"""
files = [
"тест_адресации_р11.elf",
"тест_адресации_р11.csv",
"тест_адресации_р11.аст",
"тест_адресации_р11.токены",
"тест_адресации_р11.константа",
"тест_адресации_р11_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 / "тест_адресации_р11.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: Начало теста тест_адресации_р11", 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), # Звёздочка
]
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 @@
;./тесты/тест_адресации_р11.elf "A"
;echo $? # должен быть 65
; ============================================================
; тест_адресации_r11.квс - тест косвенной адресации через R11
; ============================================================
.текст
.глобал _start
_start:
; Получаем argv[1]
вытолкнуть рсикс ; argc
вытолкнуть рсиай ; argv[0]
вытолкнуть р11 ; argv[1] в R11
; Загружаем первый байт из argv[1] через R11
загрузить ал, [р11]
; Сохраняем в переменную (не обязательно, но для проверки)
сохранить [байт], ал
; Выход с кодом = загруженный байт
переместить_имм раикс, 60
загрузить рдиай, [байт]
вызов_системы
.данные
байт: .байт 0

View file

@ -0,0 +1,138 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тест для проверки косвенной адресации через регистр R9
Запускает скомпилированную программу с различными аргументами
и проверяет код возврата (должен быть равен 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 / "тест_адресации_р9.квс"
def clean():
"""Очищает артефакты сборки"""
files = [
"тест_адресации_р9.elf",
"тест_адресации_р9.csv",
"тест_адресации_р9.аст",
"тест_адресации_р9.токены",
"тест_адресации_р9.константа",
"тест_адресации_р9_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 / "тест_адресации_р9.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: Начало теста тест_адресации_р9", 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), # Подчёркивание
]
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 @@
;./тесты/тест_адресации_р9.elf "A"
;echo $? # должен быть 65
; ============================================================
; тест_адресации_r9.квс - тест косвенной адресации через R8
; ============================================================
.текст
.глобал _start
_start:
; Получаем argv[1]
вытолкнуть рсикс ; argc
вытолкнуть рсиай ; argv[0]
вытолкнуть р9 ; argv[1] в R9
; Загружаем первый байт из argv[1] через R9
загрузить ал, [р9]
; Сохраняем в переменную (не обязательно, но для проверки)
сохранить [байт], ал
; Выход с кодом = загруженный байт
переместить_имм раикс, 60
загрузить рдиай, [байт]
вызов_системы
.данные
байт: .байт 0