kvs/kvs_pass4.py

262 lines
11 KiB
Python
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.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Четвёртый проход КВС
- Перекодирует инструкции, используя подставленные значения
- Смотрит колонки 'команда_со_значениями' и 'рассчитанный_уводящий_адрес'
- Записывает перекодированные байты в колонку 'рассчитанный_байт'
"""
import sys
import csv
import struct
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 = None
vaddr_bnd = None
labels = {} # имя метки -> адрес
label_sections = {} # имя метки -> секция
symbols = {} # имя константы -> значение
def read_csv(csv_file):
"""Читает CSV файл и возвращает список записей и заголовки"""
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):
"""Разбирает команду вида 'мнемоника операнд1, операнд2, ...'"""
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, vaddr_data, vaddr_bnd
try:
label_idx = headers.index('приводящая_метка')
addr_idx = headers.index('адрес')
segment_idx = headers.index('сегмент')
byte_idx = headers.index('байт')
except ValueError as e:
print(f"Ошибка: не найдена нужная колонка в CSV: {e}")
sys.exit(1)
# Сначала нужно определить виртуальные адреса секций
max_addr = 0
for row in entries:
addr_str = row[addr_idx].strip()
if addr_str.startswith('0x'):
addr = int(addr_str, 16)
if addr > max_addr:
max_addr = addr
segment = row[segment_idx].strip()
# Определяем vaddr_data по первому адресу в .data
if segment == '.data' and vaddr_data is None:
vaddr_data = addr
# Определяем vaddr_bnd по первому адресу в .bss
elif segment == '.bss' and vaddr_bnd is None:
vaddr_bnd = addr
if vaddr_data is None:
vaddr_data = align_up(vaddr_text + max_addr, PAGE_SIZE)
if vaddr_bnd is None:
vaddr_bnd = align_up(vaddr_data + max_addr, PAGE_SIZE)
# Собираем метки
for row in entries:
label_str = row[label_idx].strip()
if label_str:
addr_str = row[addr_idx].strip()
if addr_str.startswith('0x'):
addr = int(addr_str, 16)
for label_name in label_str.split(','):
label_name = label_name.strip()
if label_name:
labels[label_name] = addr
# Определяем секцию по адресу
if addr >= vaddr_text and addr < vaddr_text + PAGE_SIZE:
label_sections[label_name] = '.text'
elif vaddr_data and addr >= vaddr_data and addr < vaddr_data + PAGE_SIZE:
label_sections[label_name] = '.data'
elif vaddr_bnd and addr >= vaddr_bnd:
label_sections[label_name] = '.bss'
def reencode_instructions(entries, headers):
"""Перекодирует инструкции, используя подставленные значения"""
try:
source_with_values_idx = headers.index('команда_со_значениями')
calc_target_idx = headers.index('рассчитанный_уводящий_адрес')
calc_byte_idx = headers.index('рассчитанный_байт')
addr_idx = headers.index('адрес')
segment_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
addr = int(addr_str, 16)
segment = row[segment_idx].strip()
# Проверяем, есть ли команда для перекодирования
cmd = row[source_with_values_idx].strip()
calc_target = row[calc_target_idx].strip()
# Определяем, нужно ли перекодировать
need_reencode = False
if cmd:
# Есть команда с подставленными константами
need_reencode = True
elif calc_target and calc_target.startswith('0x'):
# Есть рассчитанный адрес для заглушки
# Нужно взять исходную команду и подставить адрес
source_idx = headers.index('исходная_команда')
source = row[source_idx].strip()
if source:
# Заменяем ЗАГЛУШКА имя на реальный адрес
target_addr = int(calc_target, 16)
cmd = source.replace('ЗАГЛУШКА', '').strip()
# Добавляем адрес как операнд
cmd = f"{cmd} 0x{target_addr:08x}"
need_reencode = True
if need_reencode and cmd:
try:
mnemonic, operands = parse_command(cmd)
if mnemonic:
# Вычисляем текущую позицию в секции
if segment == '.text':
current_pos = addr - vaddr_text
elif segment == '.data' and vaddr_data:
current_pos = addr - vaddr_data
else:
current_pos = 0
# Кодируем инструкцию
encoded = encode_instruction(
mnemonic, operands, labels, label_sections, symbols,
vaddr_text, vaddr_data if vaddr_data else 0,
current_pos, vaddr_bnd if vaddr_bnd else 0
)
# Записываем байты в колонку рассчитанный_байт
# Для много-байтовых инструкций записываем в последовательные строки
for j, byte in enumerate(encoded):
if i + j < len(entries):
entries[i + j][calc_byte_idx] = f"0x{byte:02x}"
else:
# Не хватает строк, создаём новую
# (в нормальной ситуации не должно происходить)
pass
reencoded_count += 1
# Пропускаем следующие строки, принадлежащие этой инструкции
# (обычно в CSV одна строка = один байт)
i += len(encoded)
continue
except Exception as e:
print(f"Ошибка перекодирования '{cmd}': {e}")
error_count += 1
# Записываем INT3 как заглушку
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):
"""Сохраняет обновлённый CSV файл"""
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):
"""Выводит статистику перекодирования"""
print(f"\n--- Статистика перекодирования ---")
print(f" Перекодировано инструкций: {reencoded_count}")
if error_count:
print(f" Ошибок: {error_count}")
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}")
# Читаем CSV
headers, entries = read_csv(csv_file)
print(f"\n--- Сбор меток из CSV ---")
collect_labels_from_csv(entries, headers)
print(f"Найдено меток: {len(labels)}")
print(f"\n--- Перекодирование инструкций ---")
reencoded_count, error_count = reencode_instructions(entries, headers)
print_statistics(reencoded_count, error_count)
# Сохраняем результат
save_csv(csv_file, headers, entries)
print(f"\nCSV сохранен: {csv_file}")
print(f"Колонка 'рассчитанный_байт' обновлена")
if __name__ == "__main__":
main()