244 lines
9.7 KiB
Python
244 lines
9.7 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
Четвёртый проход КВС
|
||
- Перекодирует инструкции, используя подставленные значения
|
||
- Смотрит колонку 'команда_со_значениями'
|
||
- Записывает перекодированные байты в колонку 'рассчитанный_байт'
|
||
- Использует виртуальные адреса из CSV для вычисления смещений
|
||
"""
|
||
|
||
import sys
|
||
import csv
|
||
|
||
sys.path.insert(0, '.')
|
||
from kvs_data import PAGE_SIZE, text_vaddr_base, align_up
|
||
from kvs_encoder import encode_instruction
|
||
|
||
# ========== ГЛОБАЛЬНОЕ СОСТОЯНИЕ ==========
|
||
vaddr_text = text_vaddr_base
|
||
vaddr_data = 0x402000
|
||
vaddr_bnd = None
|
||
|
||
labels = {}
|
||
label_sections = {}
|
||
symbols = {}
|
||
|
||
|
||
def read_csv(csv_file):
|
||
entries = []
|
||
headers = []
|
||
|
||
with open(csv_file, 'r', encoding='utf-8') as f:
|
||
reader = csv.reader(f, delimiter=';')
|
||
headers = next(reader)
|
||
for row in reader:
|
||
entries.append(row)
|
||
|
||
return headers, entries
|
||
|
||
|
||
def parse_command(cmd_str):
|
||
if not cmd_str:
|
||
return None, []
|
||
|
||
parts = cmd_str.split(' ', 1)
|
||
mnemonic = parts[0]
|
||
|
||
operands = []
|
||
if len(parts) > 1 and parts[1]:
|
||
operands = [op.strip() for op in parts[1].split(',')]
|
||
|
||
return mnemonic, operands
|
||
|
||
|
||
def collect_labels_from_csv(entries, headers):
|
||
"""
|
||
Собирает метки из CSV, используя колонку 'виртуальный_адрес'
|
||
"""
|
||
global labels, label_sections
|
||
|
||
try:
|
||
label_idx = headers.index('приводящая_метка')
|
||
virt_addr_idx = headers.index('виртуальный_адрес')
|
||
except ValueError as e:
|
||
print(f"Ошибка: не найдена нужная колонка в CSV: {e}")
|
||
sys.exit(1)
|
||
|
||
for row in entries:
|
||
label_str = row[label_idx].strip()
|
||
if label_str:
|
||
virt_addr_str = row[virt_addr_idx].strip()
|
||
if virt_addr_str.startswith('0x'):
|
||
virt_addr = int(virt_addr_str, 16)
|
||
|
||
for label_name in label_str.split(','):
|
||
label_name = label_name.strip()
|
||
if label_name:
|
||
labels[label_name] = virt_addr
|
||
label_sections[label_name] = ''
|
||
|
||
|
||
def reencode_instructions(entries, headers):
|
||
try:
|
||
source_with_values_idx = headers.index('команда_со_значениями')
|
||
calc_byte_idx = headers.index('рассчитанный_байт')
|
||
addr_idx = headers.index('адрес')
|
||
virt_addr_idx = headers.index('виртуальный_адрес')
|
||
byte_idx = headers.index('байт')
|
||
except ValueError as e:
|
||
print(f"Ошибка: не найдена нужная колонка в CSV: {e}")
|
||
sys.exit(1)
|
||
|
||
reencoded_count = 0
|
||
error_count = 0
|
||
|
||
i = 0
|
||
while i < len(entries):
|
||
row = entries[i]
|
||
addr_str = row[addr_idx].strip()
|
||
if not addr_str.startswith('0x'):
|
||
i += 1
|
||
continue
|
||
|
||
file_addr = int(addr_str, 16)
|
||
|
||
# Берём виртуальный адрес инструкции из CSV
|
||
virt_addr_str = row[virt_addr_idx].strip()
|
||
if virt_addr_str.startswith('0x'):
|
||
instr_virt_addr = int(virt_addr_str, 16)
|
||
else:
|
||
instr_virt_addr = file_addr
|
||
|
||
cmd = row[source_with_values_idx].strip()
|
||
|
||
if cmd:
|
||
try:
|
||
mnemonic, operands = parse_command(cmd)
|
||
if mnemonic:
|
||
# ОТЛАДОЧНЫЙ ВЫВОД (только для переходов)
|
||
if mnemonic.startswith('короткий_переход') or mnemonic.startswith('переход'):
|
||
print(f"\n=== ОТЛАДКА ===")
|
||
print(f" Адрес в CSV: 0x{file_addr:08x}")
|
||
print(f" Виртуальный адрес инструкции: 0x{instr_virt_addr:08x}")
|
||
print(f" Команда: {cmd}")
|
||
print(f" Мнемоника: {mnemonic}")
|
||
print(f" Операнды: {operands}")
|
||
|
||
if operands and len(operands) > 0:
|
||
target_str = operands[0]
|
||
if target_str.startswith('0x'):
|
||
target_addr = int(target_str, 16)
|
||
# Определяем размер инструкции
|
||
if mnemonic.startswith('короткий'):
|
||
instr_size = 2
|
||
else:
|
||
instr_size = 5
|
||
offset = target_addr - (instr_virt_addr + instr_size)
|
||
print(f" Целевой адрес: 0x{target_addr:08x}")
|
||
print(f" Размер инструкции: {instr_size}")
|
||
print(f" RIP = 0x{instr_virt_addr + instr_size:08x}")
|
||
print(f" Смещение = {offset} (0x{offset & 0xFFFFFFFF:08x})")
|
||
print(f" В диапазоне [-128..127]: {-128 <= offset <= 127}")
|
||
|
||
# Вычисляем current_pos как смещение в секции
|
||
# Определяем секцию по виртуальному адресу
|
||
if 0x401000 <= instr_virt_addr < 0x402000:
|
||
current_pos = instr_virt_addr - vaddr_text
|
||
elif 0x402000 <= instr_virt_addr < 0x403000:
|
||
current_pos = instr_virt_addr - vaddr_data
|
||
else:
|
||
current_pos = instr_virt_addr
|
||
|
||
# Преобразуем операнды: если строка с 0x, оставляем как есть
|
||
# Кодировщик сам разберётся с isdigit()
|
||
encoded = encode_instruction(
|
||
mnemonic, operands, labels, label_sections, symbols,
|
||
vaddr_text, vaddr_data, current_pos, vaddr_bnd
|
||
)
|
||
|
||
if mnemonic.startswith('короткий_переход') or mnemonic.startswith('переход'):
|
||
print(f" Закодировано: {[f'0x{b:02x}' for b in encoded]}")
|
||
print(f" РЕЗУЛЬТАТ: УСПЕШНО")
|
||
|
||
for j, byte in enumerate(encoded):
|
||
if i + j < len(entries):
|
||
entries[i + j][calc_byte_idx] = f"0x{byte:02x}"
|
||
|
||
reencoded_count += 1
|
||
i += len(encoded)
|
||
continue
|
||
|
||
except Exception as e:
|
||
print(f"\n=== ОТЛАДКА (ОШИБКА) ===")
|
||
print(f" Команда: {cmd}")
|
||
print(f" Мнемоника: {mnemonic if 'mnemonic' in locals() else '?'}")
|
||
print(f" Операнды: {operands if 'operands' in locals() else '?'}")
|
||
print(f" Виртуальный адрес инструкции: 0x{instr_virt_addr:08x}")
|
||
print(f" ОШИБКА: {e}")
|
||
error_count += 1
|
||
row[calc_byte_idx] = "0xcc"
|
||
i += 1
|
||
else:
|
||
# Если нет команды, копируем исходный байт
|
||
byte_str = row[byte_idx].strip()
|
||
if byte_str.startswith('0x'):
|
||
row[calc_byte_idx] = byte_str
|
||
i += 1
|
||
|
||
return reencoded_count, error_count
|
||
|
||
|
||
def save_csv(csv_file, headers, entries):
|
||
with open(csv_file, 'w', newline='', encoding='utf-8') as f:
|
||
writer = csv.writer(f, delimiter=';')
|
||
writer.writerow(headers)
|
||
writer.writerows(entries)
|
||
|
||
|
||
def print_statistics(reencoded_count, error_count, labels_count):
|
||
print(f"\n--- Статистика перекодирования ---")
|
||
print(f" Меток в словаре: {labels_count}")
|
||
print(f" Перекодировано инструкций: {reencoded_count}")
|
||
if error_count:
|
||
print(f" Ошибок: {error_count}")
|
||
else:
|
||
print(f" Ошибок: нет")
|
||
|
||
|
||
def main():
|
||
if len(sys.argv) != 2:
|
||
print("Использование: python kvs_pass4.py <входной.csv>")
|
||
sys.exit(1)
|
||
|
||
csv_file = sys.argv[1]
|
||
|
||
print(f"\n=== Четвёртый проход КВС ===")
|
||
print(f"Чтение CSV: {csv_file}")
|
||
|
||
headers, entries = read_csv(csv_file)
|
||
|
||
print(f"\n--- Сбор меток из CSV ---")
|
||
collect_labels_from_csv(entries, headers)
|
||
print(f"Найдено меток: {len(labels)}")
|
||
|
||
if labels:
|
||
print(f"\n--- Примеры меток (виртуальные адреса) ---")
|
||
for i, name in enumerate(sorted(labels.keys())[:10]):
|
||
print(f" {name} -> 0x{labels[name]:08x}")
|
||
if len(labels) > 10:
|
||
print(f" ... и ещё {len(labels) - 10}")
|
||
|
||
print(f"\n--- Перекодирование инструкций ---")
|
||
reencoded_count, error_count = reencode_instructions(entries, headers)
|
||
|
||
print_statistics(reencoded_count, error_count, len(labels))
|
||
|
||
save_csv(csv_file, headers, entries)
|
||
print(f"\nCSV сохранён: {csv_file}")
|
||
print(f"Колонка 'рассчитанный_байт' обновлена")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |