262 lines
11 KiB
Python
262 lines
11 KiB
Python
#!/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() |