247 lines
8.8 KiB
Python
247 lines
8.8 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
Верификатор КВС
|
|||
|
|
Сравнивает размеры инструкций из pass1 с реальными размерами из CSV.
|
|||
|
|
Выводит расхождения и останавливает сборку при ошибках.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
# Если True — разрешает сборку, когда единственная ошибка это общий размер .text
|
|||
|
|
# (расхождение до 16 байт считается допустимым)
|
|||
|
|
ALLOW_TEXT_SIZE_MISMATCH = True
|
|||
|
|
MAX_TEXT_SIZE_MISMATCH = 25
|
|||
|
|
|
|||
|
|
|
|||
|
|
def read_pass1(input_file):
|
|||
|
|
"""Читает pass1-файл: PARAM и LABEL"""
|
|||
|
|
params = {}
|
|||
|
|
labels = {}
|
|||
|
|
label_sections = {}
|
|||
|
|
|
|||
|
|
with open(input_file, 'r', encoding='utf-8') as f:
|
|||
|
|
for line in f:
|
|||
|
|
line = line.strip()
|
|||
|
|
if not line:
|
|||
|
|
continue
|
|||
|
|
parts = line.split(':')
|
|||
|
|
if parts[0] == "PARAM":
|
|||
|
|
key = parts[1]
|
|||
|
|
value = parts[2]
|
|||
|
|
if value.isdigit() or (value.startswith('-') and value[1:].isdigit()):
|
|||
|
|
value = int(value)
|
|||
|
|
params[key] = value
|
|||
|
|
elif parts[0] == "LABEL":
|
|||
|
|
label = parts[1]
|
|||
|
|
sec = parts[2]
|
|||
|
|
pos = int(parts[3])
|
|||
|
|
labels[label] = pos
|
|||
|
|
label_sections[label] = sec
|
|||
|
|
|
|||
|
|
return params, labels, label_sections
|
|||
|
|
|
|||
|
|
|
|||
|
|
def read_csv(input_file):
|
|||
|
|
"""
|
|||
|
|
Читает CSV-файл.
|
|||
|
|
Возвращает:
|
|||
|
|
- instr_list: список инструкций с адресами и размерами
|
|||
|
|
"""
|
|||
|
|
instr_list = []
|
|||
|
|
current_cmd = None
|
|||
|
|
current_start = None
|
|||
|
|
current_size = 0
|
|||
|
|
|
|||
|
|
with open(input_file, 'r', encoding='utf-8') as f:
|
|||
|
|
header = f.readline()
|
|||
|
|
for line in f:
|
|||
|
|
line = line.strip()
|
|||
|
|
if not line:
|
|||
|
|
continue
|
|||
|
|
parts = line.split(';')
|
|||
|
|
if len(parts) < 2:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
addr_str = parts[0]
|
|||
|
|
byte_str = parts[1]
|
|||
|
|
cmd_str = parts[3] if len(parts) > 3 else ""
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
addr = int(addr_str, 16)
|
|||
|
|
except ValueError:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if addr >= 0x402000:
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
if cmd_str:
|
|||
|
|
if current_cmd is not None:
|
|||
|
|
instr_list.append({
|
|||
|
|
'cmd': current_cmd,
|
|||
|
|
'start': current_start,
|
|||
|
|
'size': current_size
|
|||
|
|
})
|
|||
|
|
current_cmd = cmd_str
|
|||
|
|
current_start = addr
|
|||
|
|
current_size = 1
|
|||
|
|
else:
|
|||
|
|
current_size += 1
|
|||
|
|
|
|||
|
|
if current_cmd is not None:
|
|||
|
|
instr_list.append({
|
|||
|
|
'cmd': current_cmd,
|
|||
|
|
'start': current_start,
|
|||
|
|
'size': current_size
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return instr_list
|
|||
|
|
|
|||
|
|
|
|||
|
|
def verify(pass1_file, csv_file):
|
|||
|
|
"""Сравнивает pass1 и pass2, возвращает список ошибок"""
|
|||
|
|
from kvs_data import FIXED_SIZE_INSTRUCTIONS, INSTRUCTIONS
|
|||
|
|
|
|||
|
|
params, labels, label_sections = read_pass1(pass1_file)
|
|||
|
|
instr_list = read_csv(csv_file)
|
|||
|
|
|
|||
|
|
vaddr_text = params.get("vaddr_text", 0x401000)
|
|||
|
|
text_size_pass1 = params.get("text_size", 0)
|
|||
|
|
text_size_pass2 = sum(instr['size'] for instr in instr_list)
|
|||
|
|
|
|||
|
|
errors = []
|
|||
|
|
warnings = []
|
|||
|
|
|
|||
|
|
print(f"Pass1 .text: {text_size_pass1} байт")
|
|||
|
|
print(f"Pass2 .text: {text_size_pass2} байт")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
# Проверка общего размера
|
|||
|
|
text_size_error = None
|
|||
|
|
if text_size_pass1 != text_size_pass2:
|
|||
|
|
diff = text_size_pass2 - text_size_pass1
|
|||
|
|
text_size_error = f"Общий размер .text: pass1={text_size_pass1}, pass2={text_size_pass2} (разница {diff:+d} байт)"
|
|||
|
|
if ALLOW_TEXT_SIZE_MISMATCH and abs(diff) <= MAX_TEXT_SIZE_MISMATCH:
|
|||
|
|
warnings.append(text_size_error + " — допустимо, сборка продолжена")
|
|||
|
|
else:
|
|||
|
|
errors.append(text_size_error)
|
|||
|
|
|
|||
|
|
# Проверка позиций меток
|
|||
|
|
print("Проверка позиций меток (.text):")
|
|||
|
|
for label, pass1_offset in labels.items():
|
|||
|
|
if label_sections.get(label) != ".text":
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
pass1_addr = vaddr_text + pass1_offset
|
|||
|
|
|
|||
|
|
found = False
|
|||
|
|
for instr in instr_list:
|
|||
|
|
if instr['start'] == pass1_addr:
|
|||
|
|
found = True
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
if found:
|
|||
|
|
print(f" [✓] {label} — 0x{pass1_addr:x}")
|
|||
|
|
else:
|
|||
|
|
closest = None
|
|||
|
|
for instr in instr_list:
|
|||
|
|
if instr['start'] <= pass1_addr:
|
|||
|
|
closest = instr
|
|||
|
|
|
|||
|
|
if closest:
|
|||
|
|
real_addr = closest['start']
|
|||
|
|
shift = real_addr - pass1_addr
|
|||
|
|
if shift != 0:
|
|||
|
|
errors.append(f"Метка '{label}': pass1=0x{pass1_addr:x}, pass2=0x{real_addr:x} (сдвиг {shift:+d} байт)")
|
|||
|
|
print(f" [✗] {label} — pass1: 0x{pass1_addr:x}, pass2: 0x{real_addr:x} (сдвиг {shift:+d})")
|
|||
|
|
else:
|
|||
|
|
print(f" [✓] {label} — 0x{pass1_addr:x}")
|
|||
|
|
else:
|
|||
|
|
warnings.append(f"Метка '{label}' не найдена в CSV")
|
|||
|
|
print(f" [?] {label} — не найдена в CSV")
|
|||
|
|
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
# Сравнение размеров инструкций
|
|||
|
|
print("Проверка размеров инструкций:")
|
|||
|
|
|
|||
|
|
for instr in instr_list:
|
|||
|
|
cmd = instr['cmd']
|
|||
|
|
real_size = instr['size']
|
|||
|
|
real_addr = instr['start']
|
|||
|
|
|
|||
|
|
mnemonic = cmd.split()[0] if cmd else ""
|
|||
|
|
|
|||
|
|
if mnemonic in FIXED_SIZE_INSTRUCTIONS:
|
|||
|
|
pass1_size = FIXED_SIZE_INSTRUCTIONS[mnemonic]
|
|||
|
|
elif mnemonic in ("загрузить", "сохранить", "загрузить_адрес"):
|
|||
|
|
if 'MEM:reg_indirect:' in cmd:
|
|||
|
|
pass1_size = 3
|
|||
|
|
else:
|
|||
|
|
pass1_size = 7
|
|||
|
|
elif mnemonic in ("втолкнуть", "вытолкнуть"):
|
|||
|
|
parts = cmd.split()
|
|||
|
|
if len(parts) >= 2:
|
|||
|
|
reg = parts[1]
|
|||
|
|
if reg in ('р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'):
|
|||
|
|
pass1_size = 2
|
|||
|
|
else:
|
|||
|
|
pass1_size = 1
|
|||
|
|
else:
|
|||
|
|
pass1_size = 1
|
|||
|
|
elif mnemonic in ("прибавить_непосредственно", "вычесть_непосредственно", "сравнить_с"):
|
|||
|
|
pass1_size = 7
|
|||
|
|
elif mnemonic == "переместить_имм":
|
|||
|
|
pass1_size = 10
|
|||
|
|
elif mnemonic in ("переместить_с_нулями", "переместить_со_знаком"):
|
|||
|
|
pass1_size = 8
|
|||
|
|
elif mnemonic == "загрузить_байт":
|
|||
|
|
pass1_size = 2
|
|||
|
|
elif mnemonic == "втолкнуть_непосредственно":
|
|||
|
|
pass1_size = 5
|
|||
|
|
else:
|
|||
|
|
instr_info = INSTRUCTIONS.get(mnemonic)
|
|||
|
|
if instr_info and "opcode" in instr_info and instr_info["opcode"] is not None:
|
|||
|
|
pass1_size = len(instr_info["opcode"])
|
|||
|
|
else:
|
|||
|
|
pass1_size = 3
|
|||
|
|
|
|||
|
|
status = "✓" if pass1_size == real_size else "✗"
|
|||
|
|
if pass1_size != real_size:
|
|||
|
|
diff = real_size - pass1_size
|
|||
|
|
errors.append(f"Инструкция '{cmd}' на 0x{real_addr:x}: pass1={pass1_size}, pass2={real_size} (ошибка {diff:+d})")
|
|||
|
|
|
|||
|
|
print(f" [{status}] 0x{real_addr:x} {cmd} — pass1: {pass1_size}, pass2: {real_size}" +
|
|||
|
|
(f" (ошибка {real_size - pass1_size:+d})" if pass1_size != real_size else ""))
|
|||
|
|
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
return errors, warnings
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
if len(sys.argv) != 3:
|
|||
|
|
print("Использование: python kvs_verify.py <вход.проход1> <вход.csv>")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
pass1_file = sys.argv[1]
|
|||
|
|
csv_file = sys.argv[2]
|
|||
|
|
|
|||
|
|
errors, warnings = verify(pass1_file, csv_file)
|
|||
|
|
|
|||
|
|
if warnings:
|
|||
|
|
print(f"Предупреждений: {len(warnings)}")
|
|||
|
|
for w in warnings:
|
|||
|
|
print(f" ⚠ {w}")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
if errors:
|
|||
|
|
print(f"ОШИБОК: {len(errors)}")
|
|||
|
|
for e in errors:
|
|||
|
|
print(f" ❌ {e}")
|
|||
|
|
print("\nСборка остановлена. Исправьте расхождения в размерах инструкций.")
|
|||
|
|
sys.exit(1)
|
|||
|
|
else:
|
|||
|
|
print("Все размеры инструкций совпадают. Можно собирать.")
|
|||
|
|
sys.exit(0)
|