kvs/kvs_pass2.py
2026-05-02 23:04:46 +03:00

1261 lines
45 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 -*-
"""
Второй проход КВС
Генерирует машинный код и создаёт CSV-файл
"""
import sys
import struct
sys.path.insert(0, '.')
from kvs_data import INSTRUCTIONS, REGISTERS, get_reg_info, PAGE_SIZE, align_up
def unescape_string(s):
"""Преобразует escape-последовательности в реальные символы"""
result = []
i = 0
while i < len(s):
if s[i] == '\\' and i + 1 < len(s):
if s[i + 1] == 'n':
result.append('\n')
elif s[i + 1] == 't':
result.append('\t')
elif s[i + 1] == 'r':
result.append('\r')
else:
result.append(s[i])
result.append(s[i + 1])
i += 2
else:
result.append(s[i])
i += 1
return ''.join(result)
def read_ast(input_file):
"""Читает AST из файла"""
ast_lines = []
with open(input_file, 'r', encoding='utf-8') as f:
for line in f:
ast_lines.append(line.strip())
return ast_lines
def read_pass1(input_file):
"""
Читает результаты первого прохода:
- PARAM: параметры компоновки (размеры, адреса)
- LABEL: метки и их позиции
- SYMBOL: константы
"""
data = {}
labels = {}
label_sections = {}
symbols = {}
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():
value = int(value)
data[key] = value
elif parts[0] == "LABEL":
label = parts[1]
sec = parts[2]
pos = int(parts[3])
labels[label] = pos
label_sections[label] = sec
elif parts[0] == "SYMBOL":
name = parts[1]
value = int(parts[2])
symbols[name] = value
return data, labels, label_sections, symbols
def parse_operand(operand, labels, label_sections, symbols, vaddr_text, vaddr_data):
"""
Вычисляет значение операнда.
Поддерживает:
- десятичные числа (123)
- шестнадцатеричные (0x7B)
- метки (имя_метки)
- константы (из .константа)
"""
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
base = vaddr_text if section == ".text" else vaddr_data
return labels[operand] + base
if operand in symbols:
return symbols[operand]
raise ValueError("Неизвестный операнд: " + operand)
# ========== ГЕНЕРАТОРЫ МАШИННОГО КОДА ДЛЯ КАЖДОЙ ГРУППЫ ИНСТРУКЦИЙ ==========
# ----- 1. Инструкции перемещения данных (MOV, LEA, MOVZX, MOVSX) -----
def encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data):
"""
MOV reg, imm - переместить непосредственное значение в регистр
Формат: переместить_имм <регистр>, <значение|метка|константа>
"""
code = bytearray()
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data)
if size == 64:
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm & 0xFFFFFFFFFFFFFFFF))
elif size == 32:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
elif size == 16:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
code.append((imm >> 8) & 0xFF)
elif size == 8:
if reg_info.get("high8"):
code.append(0xB0 + reg + 4)
else:
if reg < 4:
code.append(0xB0 + reg)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xB0 + (reg & 7))
code.append(imm & 0xFF)
return code
def encode_mov_reg_reg(operands):
"""
MOV reg, reg - переместить данные между регистрами
Формат: переместить <регистразначения>, <регистрсточник>
"""
code = bytearray()
instr = INSTRUCTIONS["переместить"]
dst_info = get_reg_info(operands[0])
src_info = get_reg_info(operands[1])
dst = dst_info["index"]
src = src_info["index"]
size = dst_info["size"]
# Формируем REX префикс
use_66 = (size == 16)
use_rex_w = (size == 64)
rex = 0x40
if use_rex_w:
rex |= 0x08
if src >= 8:
rex |= 0x04
if dst >= 8:
rex |= 0x01
if dst_info.get("high8") or src_info.get("high8"):
rex = 0
if use_66:
code.append(0x66)
if rex != 0x40 or use_rex_w:
code.append(rex)
code.extend(instr["opcode"][-1:])
modrm = 0xC0 | ((src & 7) << 3) | (dst & 7)
code.append(modrm)
return code
def encode_mov_reg8_imm8(operands):
"""
MOV reg8, imm8 - загрузить непосредственный байт в 8-битный регистр
Формат: загрузить_байт <регистр8>, <байт>
"""
code = bytearray()
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
imm = int(operands[1]) if operands[1].isdigit() else int(operands[1], 16)
if reg_info.get("high8"):
code.append(0xB0 + reg + 4)
else:
if reg < 4:
code.append(0xB0 + reg)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xB0 + (reg & 7))
code.append(imm & 0xFF)
return code
def encode_lea(operands, labels, label_sections, symbols, vaddr_text, vaddr_data):
"""
LEA reg, mem - загрузить эффективный адрес
Формат: загрузить_адрес <регистр>, <адрес> (упрощённо - пока только метки)
"""
code = bytearray()
instr = INSTRUCTIONS["загрузить_адрес"]
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data)
# Формируем REX префикс
rex = 0x48 if reg_info["size"] == 64 else 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.extend(instr["opcode"][-1:])
# ModR/M: mod=00 (disp32), reg=reg, r/m=101 (RIP-relative)
# Упрощённо: используем абсолютный адрес через disp32
modrm = 0x05 | ((reg & 7) << 3)
code.append(modrm)
code.extend(struct.pack('<i', addr))
return code
def encode_movzx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data):
"""
MOVZX reg, mem8 - переместить с расширением нулями
Формат: переместить_сулями <регистр>, <адрес_байта>
"""
code = bytearray()
instr = INSTRUCTIONS["переместить_сулями"]
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data)
rex = 0x48 if reg_info["size"] == 64 else 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.extend(instr["opcode"])
modrm = 0x05 | ((reg & 7) << 3)
code.append(modrm)
code.extend(struct.pack('<i', addr))
return code
def encode_movsx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data):
"""
MOVSX reg, mem8 - переместить с расширением знака
Формат: переместить_со_знаком <регистр>, <адрес_байта>
"""
code = bytearray()
instr = INSTRUCTIONS["переместить_со_знаком"]
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data)
rex = 0x48 if reg_info["size"] == 64 else 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.extend(instr["opcode"])
modrm = 0x05 | ((reg & 7) << 3)
code.append(modrm)
code.extend(struct.pack('<i', addr))
return code
# ----- 2. Инструкции сравнения (CMP, TEST) -----
def encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data):
"""
CMP reg, imm - сравнить регистр с непосредственным значением
Формат: сравнить_с <регистр>, <значение|метка|константа>
"""
code = bytearray()
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data)
if size == 64:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
elif size == 32:
if -128 <= imm <= 127:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
elif size == 16:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<H', imm & 0xFFFF))
elif size == 8:
if reg_info.get("high8"):
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
else:
if reg < 4:
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x80)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
return code
def encode_cmp_reg_reg(mnemonic, operands):
"""
CMP reg, reg - сравнить регистры
Формат: сравнить <регистр1>, <регистр2>
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
dst_info = get_reg_info(operands[0])
src_info = get_reg_info(operands[1])
dst = dst_info["index"]
src = src_info["index"]
size = dst_info["size"]
use_66 = (size == 16)
use_rex_w = (size == 64)
rex = 0x40
if use_rex_w:
rex |= 0x08
if src >= 8:
rex |= 0x04
if dst >= 8:
rex |= 0x01
if dst_info.get("high8") or src_info.get("high8"):
rex = 0
if use_66:
code.append(0x66)
if rex != 0x40 or use_rex_w:
code.append(rex)
code.extend(instr["opcode"][-1:])
modrm = 0xC0 | ((src & 7) << 3) | (dst & 7)
code.append(modrm)
return code
# ----- 3. Арифметические инструкции (ADD, SUB, MUL, DIV) -----
def encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data):
"""
ADD/SUB reg, imm - прибавить/вычесть непосредственное значение
Формат: прибавить_непосредственно / вычесть_непосредственно <регистр>, <значение>
"""
code = bytearray()
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data)
op_map = {
"прибавить_непосредственно": 0x81,
"вычесть_непосредственно": 0x81,
}
op = op_map.get(mnemonic, 0x81)
subop = 0 if mnemonic == "прибавить_непосредственно" else 5 # /0 для ADD, /5 для SUB
if size == 64:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(op)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
elif size == 32:
if -128 <= imm <= 127:
op = 0x83 # короткая форма
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(op)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(op)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
elif size == 16:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(op)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<H', imm & 0xFFFF))
return code
def encode_add_sub_reg_reg(mnemonic, operands):
"""
ADD/SUB reg, reg - сложить/вычесть регистры
Формат: прибавить / вычесть <регистразначения>, <регистрсточник>
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
dst_info = get_reg_info(operands[0])
src_info = get_reg_info(operands[1])
dst = dst_info["index"]
src = src_info["index"]
size = dst_info["size"]
use_66 = (size == 16)
use_rex_w = (size == 64)
rex = 0x40
if use_rex_w:
rex |= 0x08
if src >= 8:
rex |= 0x04
if dst >= 8:
rex |= 0x01
if dst_info.get("high8") or src_info.get("high8"):
rex = 0
if use_66:
code.append(0x66)
if rex != 0x40 or use_rex_w:
code.append(rex)
code.extend(instr["opcode"][-1:])
modrm = 0xC0 | ((src & 7) << 3) | (dst & 7)
code.append(modrm)
return code
def encode_muldiv(mnemonic):
"""
MUL/IMUL/DIV/IDIV - умножение и деление (работают с RAX/RDX)
Формат: умножить / умножить_знаковое / разделить / разделить_знаковое
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
subop = instr["subop"]
code.append(0x48) # REX.W для 64-бит
code.extend(instr["opcode"])
modrm = 0xC0 | (subop << 3) | 0 # операнд в регистре (например, RBX, RCX)
code.append(modrm)
return code
# ----- 4. Логические инструкции (AND, OR, XOR, NOT, NEG) -----
def encode_and_or_xor_reg_reg(mnemonic, operands):
"""
AND/OR/XOR reg, reg - логические операции над регистрами
Формат: и / или / исключающее_или <регистразначения>, <регистрсточник>
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
dst_info = get_reg_info(operands[0])
src_info = get_reg_info(operands[1])
dst = dst_info["index"]
src = src_info["index"]
size = dst_info["size"]
use_66 = (size == 16)
use_rex_w = (size == 64)
rex = 0x40
if use_rex_w:
rex |= 0x08
if src >= 8:
rex |= 0x04
if dst >= 8:
rex |= 0x01
if dst_info.get("high8") or src_info.get("high8"):
rex = 0
if use_66:
code.append(0x66)
if rex != 0x40 or use_rex_w:
code.append(rex)
code.extend(instr["opcode"][-1:])
modrm = 0xC0 | ((src & 7) << 3) | (dst & 7)
code.append(modrm)
return code
def encode_not_neg(mnemonic, operands):
"""
NOT/NEG - инвертировать/отрицать регистр
Формат: инвертировать / отрицать <регистр>
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
subop = instr["subop"]
if size == 64:
code.append(0x48)
elif size == 16:
code.append(0x66)
if reg >= 8:
code.append(0x41)
code.extend(instr["opcode"])
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
return code
# ----- 5. Инструкции переходов и вызовов (JMP, Jcc, CALL, RET, LOOP) -----
def encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos):
"""
JMP rel32 - безусловный переход с 32-битным смещением
Формат: переход <метка>
"""
code = bytearray()
instr = INSTRUCTIONS["переход"]
code.extend(instr["opcode"])
target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data)
offset = target - (vaddr_text + current_pos + 5)
code.extend(struct.pack('<i', offset))
return code
def encode_jmp_short(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos):
"""
JMP SHORT - короткий безусловный переход с 8-битным смещением
Формат: короткий_переход <метка>
"""
code = bytearray()
code.append(0xEB)
target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data)
offset = target - (vaddr_text + current_pos + 2)
if not (-128 <= offset <= 127):
raise ValueError(f"Смещение короткого перехода {offset} вне диапазона [-128..127]")
code.append(offset & 0xFF)
return code
def encode_jcc_rel32(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos):
"""
Jcc rel32 - условный переход с 32-битным смещением
Формат: переход_если_... <метка>
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
code.extend(instr["opcode"])
target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data)
offset = target - (vaddr_text + current_pos + 6)
code.extend(struct.pack('<i', offset))
return code
def encode_jcc_short(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos):
"""
Jcc SHORT - короткий условный переход с 8-битным смещением
Формат: короткий_переход_если_... <метка>
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
code.extend(instr["opcode"])
target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data)
offset = target - (vaddr_text + current_pos + 2)
if not (-128 <= offset <= 127):
raise ValueError(f"Смещение короткого перехода {offset} вне диапазона [-128..127]")
code.append(offset & 0xFF)
return code
def encode_call(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos):
"""
CALL rel32 - вызов процедуры
Формат: вызвать <метка>
"""
code = bytearray()
instr = INSTRUCTIONS["вызвать"]
code.extend(instr["opcode"])
target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data)
offset = target - (vaddr_text + current_pos + 5)
code.extend(struct.pack('<i', offset))
return code
def encode_ret():
"""RET - возврат из процедуры"""
return INSTRUCTIONS["вернуться"]["opcode"]
def encode_loop(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos):
"""
LOOP rel8 - инструкция цикла
Формат: цикл <метка>
"""
code = bytearray()
instr = INSTRUCTIONS["цикл"]
code.extend(instr["opcode"])
target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data)
offset = target - (vaddr_text + current_pos + 2)
if not (-128 <= offset <= 127):
raise ValueError(f"Смещение LOOP {offset} вне диапазона [-128..127]")
code.append(offset & 0xFF)
return code
# ----- 6. Стековые инструкции (PUSH, POP) -----
def encode_push_reg(operands):
"""
PUSH reg - поместить регистр в стек
Формат: втолкнуть <регистр>
"""
code = bytearray()
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
if size == 64:
if reg < 8:
code.append(0x50 + reg)
else:
code.append(0x41)
code.append(0x50 + (reg - 8))
elif size == 16:
code.append(0x66)
if reg < 8:
code.append(0x50 + reg)
else:
code.append(0x41)
code.append(0x50 + (reg - 8))
else:
raise ValueError(f"PUSH не поддерживает {size}-битные регистры")
return code
def encode_pop_reg(operands):
"""
POP reg - извлечь из стека в регистр
Формат: вытолкнуть <регистр>
"""
code = bytearray()
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
if size == 64:
if reg < 8:
code.append(0x58 + reg)
else:
code.append(0x41)
code.append(0x58 + (reg - 8))
elif size == 16:
code.append(0x66)
if reg < 8:
code.append(0x58 + reg)
else:
code.append(0x41)
code.append(0x58 + (reg - 8))
else:
raise ValueError(f"POP не поддерживает {size}-битные регистры")
return code
def encode_push_imm(operands):
"""
PUSH imm - поместить непосредственное значение в стек
Формат: втолкнуть_непосредственно <значение>
"""
code = bytearray()
imm = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16)
if -128 <= imm <= 127:
code.append(0x6A) # PUSH imm8
code.append(imm & 0xFF)
else:
code.append(0x68) # PUSH imm32
code.extend(struct.pack('<i', imm))
return code
# ----- 7. Битовые операции (SHL, SHR, SAR, ROL, ROR) -----
def encode_shift_rotate(mnemonic, operands):
"""
SHL/SHR/SAR/ROL/ROR - сдвиги и вращения
Формат: сдвиг_влево <регистр>, <количество>
"""
code = bytearray()
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
shift = int(operands[1]) if operands[1].isdigit() else int(operands[1], 16)
# REX.W только для 64-битных регистров
if size == 64:
code.append(0x48)
# Опкод C1 для сдвига с imm8
code.append(0xC1)
# subop в зависимости от типа
subop_map = {
"сдвиг_влево": 4, # SHL = /4
"сдвиг_вправо": 5, # SHR = /5
"сдвиг_арифметический_вправо": 7, # SAR = /7
"вращать_влево": 0, # ROL = /0
"вращать_вправо": 1, # ROR = /1
}
subop = subop_map.get(mnemonic, 0)
# ModR/M: mod=11, reg=subop, r/m=reg
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
code.append(shift & 0xFF)
return bytes(code)
# ----- 8. Системные и отладочные инструкции -----
def encode_syscall():
"""SYSCALL - системный вызов"""
return INSTRUCTIONS["вызов_системы"]["opcode"]
def encode_int(operands):
"""
INT imm8 - прерывание
Формат: прервать <номер>
"""
code = bytearray()
instr = INSTRUCTIONS["прервать"]
code.extend(instr["opcode"])
imm = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16)
code.append(imm & 0xFF)
return code
def encode_hlt():
"""HLT - остановить процессор"""
return INSTRUCTIONS["остановить"]["opcode"]
def encode_int3():
"""INT3 - точка останова (отладка)"""
return INSTRUCTIONS["отладка"]["opcode"]
def encode_nop():
"""NOP - нет операции"""
return INSTRUCTIONS["нет_операции"]["opcode"]
# ----- 9. Инструкции работы с флагами -----
def encode_flag_instruction(mnemonic):
"""STC/CLC/STD/CLD/PUSHF/POPF - инструкции работы с флагами"""
return INSTRUCTIONS[mnemonic]["opcode"]
# ----- 10. Ввод-вывод -----
def encode_in(operands):
"""
IN imm8 - ввод байта из порта
Формат: ввод_байта <порт>
"""
code = bytearray()
instr = INSTRUCTIONS["ввод_байта"]
code.extend(instr["opcode"])
port = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16)
code.append(port & 0xFF)
return code
def encode_out(operands):
"""
OUT imm8 - вывод байта в порт
Формат: вывод_байта <порт>
"""
code = bytearray()
instr = INSTRUCTIONS["вывод_байта"]
code.extend(instr["opcode"])
port = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16)
code.append(port & 0xFF)
return code
# ----- 11. Строковые инструкции -----
def encode_string_instruction(mnemonic):
"""MOVSB/MOVSW/CMPSB/SCASB - строковые инструкции"""
return INSTRUCTIONS[mnemonic]["opcode"]
# ----- 12. Инструкции идентификации процессора -----
def encode_cpuid():
"""CPUID - идентификация процессора"""
return INSTRUCTIONS["идентифицировать_процессор"]["opcode"]
def encode_rdtsc():
"""RDTSC - чтение счётчика времени"""
return INSTRUCTIONS["прочитать_счётчик"]["opcode"]
# ----- 13. Инструкции с памятью (упрощённые) -----
def encode_xchg(operands):
"""
XCHG reg, reg - обменять регистры
Формат: обменять <регистр1>, <регистр2>
"""
code = bytearray()
instr = INSTRUCTIONS["обменять"]
reg1_info = get_reg_info(operands[0])
reg2_info = get_reg_info(operands[1])
reg1 = reg1_info["index"]
reg2 = reg2_info["index"]
rex = 0x48 if reg1_info["size"] == 64 else 0x40
if reg1 >= 8 or reg2 >= 8:
rex |= 0x01
if reg2 >= 8:
rex |= 0x04
code.append(rex)
code.extend(instr["opcode"])
modrm = 0xC0 | ((reg2 & 7) << 3) | (reg1 & 7)
code.append(modrm)
return code
# ========== ГЛАВНЫЙ ДИСПЕТЧЕР ==========
def encode_instruction(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos):
"""
Главный диспетчер генерации машинного кода.
По мнемонике выбирает соответствующую функцию генерации.
"""
# ----- Инструкции без операндов -----
if mnemonic == "вызов_системы":
return encode_syscall()
elif mnemonic == "нет_операции":
return encode_nop()
elif mnemonic == "вернуться":
return encode_ret()
elif mnemonic == "остановить":
return encode_hlt()
elif mnemonic == "отладка":
return encode_int3()
elif mnemonic in ("установить_перенос", "сбросить_перенос", "установить_направление",
"сбросить_направление", "втолкнуть_флаги", "вытолкнуть_флаги"):
return encode_flag_instruction(mnemonic)
elif mnemonic in ("переместить_байт", "переместить_слово", "сравнить_байты", "сканировать_байт"):
return encode_string_instruction(mnemonic)
elif mnemonic == "идентифицировать_процессор":
return encode_cpuid()
elif mnemonic == "прочитать_счётчик":
return encode_rdtsc()
# ----- MOV (разные формы) -----
elif mnemonic == "переместить_имм":
return encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data)
elif mnemonic == "переместить":
return encode_mov_reg_reg(operands)
elif mnemonic == "загрузить_байт":
return encode_mov_reg8_imm8(operands)
elif mnemonic == "загрузить_адрес":
return encode_lea(operands, labels, label_sections, symbols, vaddr_text, vaddr_data)
elif mnemonic == "переместить_сулями":
return encode_movzx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data)
elif mnemonic == "переместить_со_знаком":
return encode_movsx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data)
# ----- CMP (сравнение) -----
elif mnemonic == "сравнить_с":
return encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data)
elif mnemonic in ("сравнить", "сравнить_байт", "проверить"):
return encode_cmp_reg_reg(mnemonic, operands)
# ----- ADD/SUB (арифметика) -----
elif mnemonic in ("прибавить_непосредственно", "вычесть_непосредственно"):
return encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data)
elif mnemonic in ("прибавить", "вычесть", "прибавить_байт", "вычесть_байт"):
return encode_add_sub_reg_reg(mnemonic, operands)
# ----- MUL/DIV -----
elif mnemonic in ("умножить", "умножить_знаковое", "разделить", "разделить_знаковое"):
return encode_muldiv(mnemonic)
# ----- AND/OR/XOR -----
elif mnemonic in ("и", "или", "исключающее_или"):
return encode_and_or_xor_reg_reg(mnemonic, operands)
# ----- NOT/NEG -----
elif mnemonic in ("инвертировать", "отрицать"):
return encode_not_neg(mnemonic, operands)
# ----- INC/DEC -----
elif mnemonic in ("увеличить", "уменьшить"):
return encode_incdec(mnemonic, operands)
# ----- Переходы -----
elif mnemonic == "переход":
return encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos)
elif mnemonic == "короткий_переход":
return encode_jmp_short(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos)
elif mnemonic in ("переход_если_равно", "переход_если_неравно", "переход_если_меньше",
"переход_если_больше", "переход_если_меньше_или_равно", "переход_если_больше_или_равно",
"переход_если_перенос", "переход_если_нет_переноса", "переход_если_ноль", "переход_если_неоль"):
return encode_jcc_rel32(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos)
elif mnemonic in ("короткий_переход_если_равно", "короткий_переход_если_неравно",
"короткий_переход_если_меньше", "короткий_переход_если_больше",
"короткий_переход_если_меньше_или_равно", "короткий_переход_если_больше_или_равно",
"короткий_переход_если_перенос", "короткий_переход_если_нет_переноса",
"короткий_переход_если_ноль", "короткий_переход_если_неоль"):
return encode_jcc_short(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos)
# ----- CALL/RET/LOOP -----
elif mnemonic == "вызвать":
return encode_call(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos)
elif mnemonic == "цикл":
return encode_loop(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos)
# ----- PUSH/POP -----
elif mnemonic == "втолкнуть":
return encode_push_reg(operands)
elif mnemonic == "вытолкнуть":
return encode_pop_reg(operands)
elif mnemonic == "втолкнуть_непосредственно":
return encode_push_imm(operands)
# ----- Битовые операции -----
elif mnemonic in ("сдвиг_влево", "сдвиг_вправо", "сдвиг_арифметический_вправо",
"вращать_влево", "вращать_вправо"):
return encode_shift_rotate(mnemonic, operands)
# ----- Ввод-вывод -----
elif mnemonic == "ввод_байта":
return encode_in(operands)
elif mnemonic == "вывод_байта":
return encode_out(operands)
# ----- Прочие -----
elif mnemonic == "прервать":
return encode_int(operands)
elif mnemonic == "обменять":
return encode_xchg(operands)
else:
raise NotImplementedError(f"Инструкция '{mnemonic}' не реализована")
def encode_incdec(mnemonic, operands):
"""
INC / DEC - инкремент и декремент регистра
Формат: увеличить <регистр> / уменьшить <регистр>
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
reg_info = get_reg_info(operands[0])
reg = reg_info["index"]
size = reg_info["size"]
subop = instr["subop"]
if size == 64:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFF)
elif size == 32:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 16:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 8:
if reg_info.get("high8"):
code.append(0xFE)
else:
if reg < 4:
code.append(0xFE)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFE)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
return bytes(code)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
return bytes(code)
# ========== ОСНОВНОЙ КЛАСС ВТОРОГО ПРОХОДА ==========
class Pass2:
def __init__(self, pass1_data, labels, label_sections, symbols):
self.labels = labels
self.label_sections = label_sections
self.symbols = symbols
self.vaddr_text = pass1_data["vaddr_text"]
text_size = pass1_data["text_size"]
self.vaddr_data = align_up(self.vaddr_text + text_size, PAGE_SIZE)
self.position = {".text": 0, ".data": 0}
self.current_section = ".text"
self.csv_lines = []
self.data_bytes = {".text": bytearray(), ".data": bytearray()}
def get_target_addr(self, mnemonic, operands):
"""Вычисляет целевой адрес для инструкций перехода"""
jump_instructions = {
"переход", "короткий_переход", "вызвать", "цикл",
"переход_если_равно", "переход_если_неравно", "переход_если_меньше",
"переход_если_больше", "переход_если_меньше_или_равно", "переход_если_больше_или_равно",
"переход_если_перенос", "переход_если_нет_переноса", "переход_если_ноль", "переход_если_неоль",
"короткий_переход_если_равно", "короткий_переход_если_неравно", "короткий_переход_если_меньше",
"короткий_переход_если_больше", "короткий_переход_если_меньше_или_равно", "короткий_переход_если_больше_или_равно",
"короткий_переход_если_перенос", "короткий_переход_если_нет_переноса",
"короткий_переход_если_ноль", "короткий_переход_если_неоль"
}
try:
if mnemonic in jump_instructions:
target = parse_operand(operands[0], self.labels, self.label_sections,
self.symbols, self.vaddr_text, self.vaddr_data)
return hex(target)
elif mnemonic == "переместить_имм" and len(operands) >= 2:
op_str = operands[1]
if op_str in self.labels:
sec = self.label_sections[op_str]
addr = self.labels[op_str] + (self.vaddr_text if sec == ".text" else self.vaddr_data)
return hex(addr)
elif op_str in self.symbols:
return hex(self.symbols[op_str])
except Exception:
return "ошибка"
return ""
def process_line(self, line):
"""Обрабатывает одну строку AST"""
parts = line.split(':')
line_type = parts[0]
if line_type == "DIRECTIVE":
directive = parts[1]
if directive == '.текст':
self.current_section = ".text"
elif directive == '.данные':
self.current_section = ".data"
elif directive in ('.строкауль', '.строка'):
s = parts[3] if len(parts) > 3 else ""
real_s = unescape_string(s)
bstring = real_s.encode('utf-8')
if directive == '.строкауль':
bstring += b'\x00'
original_cmd = f'{directive} "{s}"'
start_addr = self.vaddr_data + self.position[".data"]
for i, byte in enumerate(bstring):
addr = start_addr + i
byte_hex = "{:02X}".format(byte)
cmd = original_cmd if i == 0 else ""
self.csv_lines.append((hex(addr), byte_hex, "", cmd))
self.data_bytes[".data"] += bstring
self.position[".data"] += len(bstring)
elif directive == '.байт':
if len(parts) > 3 and parts[3]:
bytes_str = parts[3]
byte_values = bytes_str.split(',')
original_cmd = f".байт {bytes_str}"
start_addr = self.vaddr_data + self.position[".data"]
for i, byte_str in enumerate(byte_values):
if byte_str.startswith('0x'):
val = int(byte_str, 16)
elif byte_str.isdigit() or (byte_str.startswith('-') and byte_str[1:].isdigit()):
val = int(byte_str)
else:
continue
addr = start_addr + i
byte_hex = "{:02X}".format(val & 0xFF)
cmd = original_cmd if i == 0 else ""
self.csv_lines.append((hex(addr), byte_hex, "", cmd))
self.data_bytes[".data"].append(val & 0xFF)
self.position[".data"] += len(byte_values)
elif line_type == "LABEL":
label_name = parts[1]
sec = parts[2]
if label_name in self.labels:
self.position[sec] = self.labels[label_name]
elif line_type == "INSTR":
mnemonic = parts[1]
sec = parts[2]
operands = []
if len(parts) > 3 and parts[3]:
operands = parts[3].split(',')
if operands:
original_cmd = f"{mnemonic} {', '.join(operands)}"
else:
original_cmd = mnemonic
current_pos = self.position[sec]
code = encode_instruction(mnemonic, operands, self.labels, self.label_sections,
self.symbols, self.vaddr_text, self.vaddr_data, current_pos)
target_addr = self.get_target_addr(mnemonic, operands)
start_addr = (self.vaddr_text if sec == ".text" else self.vaddr_data) + current_pos
for i, byte in enumerate(code):
addr = start_addr + i
byte_hex = "{:02X}".format(byte)
cmd = original_cmd if i == 0 else ""
addr_target = target_addr if i == 0 else ""
self.csv_lines.append((hex(addr), byte_hex, addr_target, cmd))
self.data_bytes[sec] += code
self.position[sec] += len(code)
def process_all(self, ast_lines):
"""Обрабатывает все строки AST"""
for line in ast_lines:
self.process_line(line)
return self.csv_lines
def write_csv(csv_lines, output_file):
"""Записывает сгенерированные байты в CSV файл"""
with open(output_file, 'w', encoding='utf-8') as f:
f.write("адрес;байт;целевой_адрессходная_команда\n")
for addr, byte, target, cmd in csv_lines:
if cmd and (';' in cmd or '\n' in cmd):
cmd = '"' + cmd.replace('"', '""') + '"'
f.write(f"{addr};{byte};{target};{cmd}\n")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Использование: python kvs_pass2.py <вход.аст> <вход.проход1> <выход.csv>")
sys.exit(1)
ast_lines = read_ast(sys.argv[1])
pass1_data, labels, label_sections, symbols = read_pass1(sys.argv[2])
pass2 = Pass2(pass1_data, labels, label_sections, symbols)
csv_lines = pass2.process_all(ast_lines)
write_csv(csv_lines, sys.argv[3])
print(f"Проход 2: {len(csv_lines)} строк CSV записано в {sys.argv[3]}")
print(f" .text: {pass2.position['.text']} байт, виртуальный адрес: 0x{pass2.vaddr_text:x}")
print(f" .data: {pass2.position['.data']} байт, виртуальный адрес: 0x{pass2.vaddr_data:x}")