кодировщик инструкций фиксированного размера

This commit is contained in:
mzhoot 2026-05-17 12:21:28 +03:00
parent 5f7d212dbf
commit e1259f0b88
7 changed files with 704 additions and 602 deletions

View file

@ -238,6 +238,77 @@ INSTRUCTIONS = {
}
# Размеры инструкций в байтах (для первого прохода)
INSTRUCTION_SIZES = {
"переместить_имм": 10,
"сравнить_с": 7,
"переход": 5,
"переход_если_равно": 6,
"переход_если_неравно": 6,
"переход_если_ноль": 6,
"переход_если_неоль": 6,
"переход_если_меньше": 6,
"переход_если_больше": 6,
"переход_если_меньше_или_равно": 6,
"переход_если_больше_или_равно": 6,
"переход_если_перенос": 6,
"переход_если_нет_переноса": 6,
"вызов_системы": 2,
"нет_операции": 1,
"короткий_переход": 2,
"короткий_переход_если_равно": 2,
"короткий_переход_если_неравно": 2,
"короткий_переход_если_меньше": 2,
"короткий_переход_если_больше": 2,
"короткий_переход_если_меньше_или_равно": 2,
"короткий_переход_если_больше_или_равно": 2,
"короткий_переход_если_перенос": 2,
"короткий_переход_если_нет_переноса": 2,
"короткий_переход_если_ноль": 2,
"короткий_переход_если_неоль": 2,
"сравнить": 3,
"проверить": 3,
"вычесть": 3,
"прибавить": 3,
"увеличить": 3,
"уменьшить": 3,
"и": 3,
"или": 3,
"исключающее_или": 3,
"инвертировать": 3,
"отрицать": 3,
"втолкнуть": 2,
"вытолкнуть": 2,
"втолкнуть_непосредственно": 5,
"умножить": 3,
"умножить_знаковое": 3,
"разделить": 3,
"разделить_знаковое": 3,
"сдвиг_влево": 4,
"сдвиг_вправо": 4,
"сдвиг_арифметический_вправо": 4,
"вращать_влево": 4,
"вращать_вправо": 4,
"цикл": 2,
"обменять": 4,
"прервать": 2,
"ввод_байта": 2,
"вывод_байта": 2,
"установить_перенос": 1,
"сбросить_перенос": 1,
"установить_направление": 1,
"сбросить_направление": 1,
"втолкнуть_флаги": 1,
"вытолкнуть_флаги": 1,
"переместить_байт": 1,
"переместить_слово": 1,
"сравнить_байты": 1,
"сканировать_байт": 1,
"идентифицировать_процессор": 2,
"прочитать_счётчик": 2,
}
# === ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ===
def get_reg_info(reg_name):

View file

@ -16,25 +16,43 @@
"""
import sys
import re
def error_unterminated_string(line_num):
return f"Незакрытая кавычка в строке на строке {line_num}"
def is_hex_number(s):
"""Проверяет, является ли строка шестнадцатеричным числом (0x... или 0X...)"""
return re.match(r'^0[xX][0-9a-fA-F]+$', s) is not None
if len(s) < 3:
return False
if not (s[0] == '0' and (s[1] == 'x' or s[1] == 'X')):
return False
for ch in s[2:]:
if not (('0' <= ch <= '9') or ('a' <= ch <= 'f') or ('A' <= ch <= 'F')):
return False
return True
def is_dec_number(s):
"""Проверяет, является ли строка десятичным числом (возможно с минусом)"""
if s.startswith('-'):
s = s[1:]
return s.isdigit()
if not s:
return False
start = 0
if s[0] == '-':
if len(s) == 1:
return False
start = 1
for ch in s[start:]:
if ch < '0' or ch > '9':
return False
return True
def tokenize_line(line, line_num):
"""Разбирает строку в токены"""
# Удаляем комментарии
semi = line.find(';')
semi = -1
for idx in range(len(line)):
if line[idx] == ';':
semi = idx
break
if semi != -1:
line = line[:semi]
line = line.rstrip()
@ -49,7 +67,7 @@ def tokenize_line(line, line_num):
ch = line[i]
# Пропускаем пробелы
if ch.isspace():
if ch == ' ' or ch == '\t':
i += 1
continue
@ -81,7 +99,7 @@ def tokenize_line(line, line_num):
i += 1
continue
# НОВЫЕ ТОКЕНЫ ДЛЯ СЛОЖНОЙ АДРЕСАЦИИ
# ТОКЕНЫ ДЛЯ СЛОЖНОЙ АДРЕСАЦИИ
if ch == '[':
tokens.append(('LBRACKET', '['))
i += 1
@ -98,12 +116,6 @@ def tokenize_line(line, line_num):
continue
if ch == '-':
# Проверяем, не является ли это началом отрицательного числа
# Смотрим следующий символ
if i + 1 < n and (line[i + 1].isdigit() or line[i + 1] == '0'):
# Возможно отрицательное число, дадим разобраться дальше
# Но для простоты сохраняем как MINUS, а число будет отдельным токеном
pass
tokens.append(('MINUS', '-'))
i += 1
continue
@ -115,7 +127,7 @@ def tokenize_line(line, line_num):
# Слова, числа, метки
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;[]+*-'):
while j < n and not (line[j] in ' \t,;:[]+*-'):
j += 1
word = line[i:j]

View file

@ -7,9 +7,8 @@
"""
import sys
import re
sys.path.insert(0, '.')
from kvs_data import PAGE_SIZE, text_vaddr_base, data_vaddr_base, align_up, INSTRUCTIONS
from kvs_data import PAGE_SIZE, text_vaddr_base, align_up, INSTRUCTIONS, INSTRUCTION_SIZES
def unescape_string(s):
"""Преобразует escape-последовательности в реальные символы"""
@ -39,6 +38,56 @@ def read_ast(input_file):
ast_lines.append(line.strip())
return ast_lines
def is_hex_number(s):
"""Проверяет, является ли строка шестнадцатеричным числом"""
if len(s) < 3:
return False
if not (s[0] == '0' and (s[1] == 'x' or s[1] == 'X')):
return False
for ch in s[2:]:
if not (('0' <= ch <= '9') or ('a' <= ch <= 'f') or ('A' <= ch <= 'F')):
return False
return True
def is_dec_number(s):
"""Проверяет, является ли строка десятичным числом (возможно с минусом)"""
if not s:
return False
start = 0
if s[0] == '-':
if len(s) == 1:
return False
start = 1
for ch in s[start:]:
if ch < '0' or ch > '9':
return False
return True
def is_register(s):
"""Проверяет, является ли строка именем регистра"""
return s in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']
def split_by_operators(content):
"""
Разбивает строку по операторам + и - с сохранением операторов.
Аналог re.split(r'([+\-])', content)
Пример: "раикс + рбикс*4 - 8" -> ["раикс ", "+", " рбикс*4 ", "-", " 8"]
"""
parts = []
current = ''
for ch in content:
if ch == '+' or ch == '-':
if current:
parts.append(current)
parts.append(ch)
current = ''
else:
current += ch
if current:
parts.append(current)
return parts
def parse_memory_operand(operand_str):
"""
Анализирует операнд памяти вида [reg + reg*scale + disp]
@ -71,9 +120,9 @@ def parse_memory_operand(operand_str):
}
# Проверяем, не является ли содержимое просто числом (абсолютный адрес)
if content.isdigit() or (content.startswith('0x') and len(content) > 2 and content[2:].replace('0','').replace('1','').replace('2','').replace('3','').replace('4','').replace('5','').replace('6','').replace('7','').replace('8','').replace('9','').replace('a','').replace('b','').replace('c','').replace('d','').replace('e','').replace('f','').replace('A','').replace('B','').replace('C','').replace('D','').replace('E','').replace('F','') == ''):
if is_dec_number(content) or is_hex_number(content):
result['has_disp'] = True
if content.startswith('0x'):
if content.startswith('0x') or content.startswith('0X'):
result['disp_value'] = int(content, 16)
else:
result['disp_value'] = int(content)
@ -81,43 +130,41 @@ def parse_memory_operand(operand_str):
return result
# Простой регистр без смещения
if content in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']:
if is_register(content):
result['has_base'] = True
result['base_reg'] = content
return result
# Разбираем выражение: регистр [+- регистр[*масштаб] [+- смещение]]
parts = re.split(r'([+\-])', content)
parts = split_by_operators(content)
for part in parts:
part = part.strip()
if not part or part in '+-':
if not part or part == '+' or part == '-':
continue
if part in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']:
if is_register(part):
if not result['has_base']:
result['has_base'] = True
result['base_reg'] = part
elif not result['has_index']:
result['has_index'] = True
result['index_reg'] = part
elif part.isdigit() or (part.startswith('0x') and len(part) > 2):
elif is_dec_number(part) or is_hex_number(part):
result['has_disp'] = True
if part.startswith('0x'):
if part.startswith('0x') or part.startswith('0X'):
result['disp_value'] = int(part, 16)
else:
result['disp_value'] = int(part)
elif '*' in part:
reg_part, scale_part = part.split('*')
reg_part = reg_part.strip()
scale_part = scale_part.strip()
if reg_part in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']:
result['has_index'] = True
result['index_reg'] = reg_part
result['scale'] = int(scale_part)
star_pos = part.find('*')
if star_pos != -1:
reg_part = part[:star_pos].strip()
scale_part = part[star_pos + 1:].strip()
if is_register(reg_part):
result['has_index'] = True
result['index_reg'] = reg_part
result['scale'] = int(scale_part)
if result['has_disp']:
if -128 <= result['disp_value'] <= 127:
@ -179,16 +226,20 @@ class Pass1:
elif directive == '.константа':
name = parts[2]
value_str = parts[3]
if value_str.isdigit():
if is_dec_number(value_str):
self.symbols[name] = int(value_str)
elif value_str.startswith('0x'):
elif is_hex_number(value_str):
self.symbols[name] = int(value_str, 16)
else:
self.symbols[name] = 0
elif directive == '.байт':
sec = parts[2]
if len(parts) > 3 and parts[3]:
num_bytes = len(parts[3].split(','))
# Считаем количество значений, разделённых запятыми
num_bytes = 1
for ch in parts[3]:
if ch == ',':
num_bytes += 1
self.position[sec] += num_bytes
# Директивы резервирования в .бнд
@ -218,7 +269,18 @@ class Pass1:
mnemonic = parts[1]
sec = parts[2]
operands_str = parts[3] if len(parts) > 3 else ""
operands = operands_str.split(',') if operands_str else []
operands = []
if operands_str:
# Разбиваем по запятым
current = ''
for ch in operands_str:
if ch == ',':
operands.append(current)
current = ''
else:
current += ch
if current:
operands.append(current)
size = self.estimate_size(mnemonic, operands)
self.position[sec] += size
@ -230,83 +292,15 @@ class Pass1:
# RIP-relative: REX (1) + opcode (1) + ModR/M (1) + disp32 (4) = 7
return 7
# Инструкции без операндов
# Инструкции без операндов — размер из INSTRUCTIONS
if mnemonic in ("вызов_системы", "нет_операции", "вернуться", "остановить", "отладка"):
instr = INSTRUCTIONS.get(mnemonic)
if instr and "opcode" in instr:
return len(instr["opcode"])
return 2
# Таблица фиксированных размеров
size_map = {
"переместить_имм": 10,
"сравнить_с": 7,
"переход": 5,
"переход_если_равно": 6,
"переход_если_неравно": 6,
"переход_если_ноль": 6,
"переход_если_неоль": 6,
"переход_если_меньше": 6,
"переход_если_больше": 6,
"переход_если_меньше_или_равно": 6,
"переход_если_больше_или_равно": 6,
"переход_если_перенос": 6,
"переход_если_нет_переноса": 6,
"вызов_системы": 2,
"нет_операции": 1,
"короткий_переход": 2,
"короткий_переход_если_равно": 2,
"короткий_переход_если_неравно": 2,
"короткий_переход_если_меньше": 2,
"короткий_переход_если_больше": 2,
"короткий_переход_если_меньше_или_равно": 2,
"короткий_переход_если_больше_или_равно": 2,
"короткий_переход_если_перенос": 2,
"короткий_переход_если_нет_переноса": 2,
"короткий_переход_если_ноль": 2,
"короткий_переход_если_неоль": 2,
"сравнить": 3,
"проверить": 3,
"вычесть": 3,
"прибавить": 3,
"увеличить": 3,
"уменьшить": 3,
"и": 3,
"или": 3,
"исключающее_или": 3,
"инвертировать": 3,
"отрицать": 3,
"втолкнуть": 2,
"вытолкнуть": 2,
"втолкнуть_непосредственно": 5,
"умножить": 3,
"умножить_знаковое": 3,
"разделить": 3,
"разделить_знаковое": 3,
"сдвиг_влево": 4,
"сдвиг_вправо": 4,
"сдвиг_арифметический_вправо": 4,
"вращать_влево": 4,
"вращать_вправо": 4,
"цикл": 2,
"обменять": 4,
"прервать": 2,
"ввод_байта": 2,
"вывод_байта": 2,
"установить_перенос": 1,
"сбросить_перенос": 1,
"установить_направление": 1,
"сбросить_направление": 1,
"втолкнуть_флаги": 1,
"вытолкнуть_флаги": 1,
"переместить_байт": 1,
"переместить_слово": 1,
"сравнить_байты": 1,
"сканировать_байт": 1,
"идентифицировать_процессор": 2,
"прочитать_счётчик": 2,
}
return size_map.get(mnemonic, 3)
# Фиксированные размеры из общей таблицы
return INSTRUCTION_SIZES.get(mnemonic, 3)
def calculate_layout(self):
text_size = self.position[".text"]

View file

@ -91,11 +91,8 @@ class Pass2:
self.symbols = symbols
self.vaddr_text = pass1_data["vaddr_text"]
text_size = pass1_data["text_size"]
data_size = pass1_data["data_size"]
self.vaddr_data = align_up(self.vaddr_text + text_size, PAGE_SIZE)
self.vaddr_bnd = pass1_data.get("vaddr_bnd", align_up(self.vaddr_data + data_size, PAGE_SIZE))
self.bnd_size = pass1_data.get("bnd_size", 0)
self.position = {".text": 0, ".data": 0, ".бнд": 0}
self.position = {".text": 0, ".data": 0}
self.current_section = ".text"
self.csv_lines = []
self.data_bytes = {".text": bytearray(), ".data": bytearray()}
@ -122,14 +119,7 @@ class Pass2:
op_str = operands[1]
if op_str in self.labels:
sec = self.label_sections[op_str]
if sec == ".text":
addr = self.labels[op_str] + self.vaddr_text
elif sec == ".data":
addr = self.labels[op_str] + self.vaddr_data
elif sec == ".бнд":
addr = self.labels[op_str] + self.vaddr_bnd
else:
addr = self.labels[op_str] + self.vaddr_text
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])
@ -149,8 +139,6 @@ class Pass2:
self.current_section = ".text"
elif directive == '.данные':
self.current_section = ".data"
elif directive == '.бнд':
self.current_section = ".бнд"
elif directive in ('.строкауль', '.строка'):
s = parts[3] if len(parts) > 3 else ""
real_s = unescape_string(s)
@ -192,23 +180,6 @@ class Pass2:
self.data_bytes[".data"].append(val & 0xFF)
self.position[".data"] += len(byte_values)
# Директивы резервирования в .бнд — не генерируют байты
elif directive == '.резб':
count = int(parts[3])
self.position['.бнд'] += count * 1
elif directive == '.резс':
count = int(parts[3])
self.position['.бнд'] += count * 2
elif directive == '.рездс':
count = int(parts[3])
self.position['.бнд'] += count * 4
elif directive == '.резкс':
count = int(parts[3])
self.position['.бнд'] += count * 8
elif line_type == "LABEL":
label_name = parts[1]
@ -278,6 +249,4 @@ if __name__ == "__main__":
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}")
if pass2.bnd_size > 0:
print(f" .бнд: {pass2.bnd_size} байт, виртуальный адрес: 0x{pass2.vaddr_bnd:x}")
print(f" .data: {pass2.position['.data']} байт, виртуальный адрес: 0x{pass2.vaddr_data:x}")

View file

@ -3,14 +3,44 @@
"""
Кодировщик инструкций для КВС
Содержит все функции генерации машинного кода
Содержит все функции генерации машинного кода.
Фиксированные инструкции вынесены в kvs_pass2_encoder_fixsize.py.
Здесь остаются: parse_operand, parse_memory_operand, инструкции с адресацией, диспетчер.
"""
import struct
import sys
from kvs_data import INSTRUCTIONS, REGISTERS, get_reg_info
from kvs_pass2_encoder_fixsize import (
encode_mov_reg_reg,
encode_mov_reg8_imm8,
encode_cmp_reg_reg,
encode_add_sub_reg_reg,
encode_muldiv,
encode_and_or_xor_reg_reg,
encode_not_neg,
encode_incdec,
encode_push_reg,
encode_pop_reg,
encode_push_imm,
encode_shift_rotate,
encode_syscall,
encode_int,
encode_hlt,
encode_int3,
encode_nop,
encode_flag_instruction,
encode_in,
encode_out,
encode_string_instruction,
encode_cpuid,
encode_rdtsc,
encode_xchg,
)
# ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ==========
def parse_operand(operand, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None):
"""
Вычисляет значение операнда.
@ -96,6 +126,8 @@ def parse_memory_operand(operand_str, labels, label_sections, vaddr_text, vaddr_
return None
# ========== MOV С ПАМЯТЬЮ ==========
def encode_mov_reg_mem_absolute(reg_info, mem_info, current_pos, vaddr_text):
"""
Кодирует MOV reg, [addr]
@ -169,12 +201,10 @@ def encode_mov_reg_mem(operands, labels, label_sections, symbols, vaddr_text, va
reg_info = get_reg_info(operands[0])
mem_operand = operands[1]
# Пытаемся разобрать операнд памяти
mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd)
if mem_info and mem_info['type'] == 'absolute':
return encode_mov_reg_mem_absolute(reg_info, mem_info, current_pos, vaddr_text)
# TODO: другие типы адресации
print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr)
return b'\x90' * 3
@ -187,12 +217,10 @@ def encode_mov_mem_reg(operands, labels, label_sections, symbols, vaddr_text, va
mem_operand = operands[0]
reg_info = get_reg_info(operands[1])
# Пытаемся разобрать операнд памяти
mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd)
if mem_info and mem_info['type'] == 'absolute':
return encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text)
# TODO: другие типы адресации
print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr)
return b'\x90' * 3
@ -205,7 +233,6 @@ def encode_lea_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_
reg_info = get_reg_info(operands[0])
mem_operand = operands[1]
# Пытаемся разобрать операнд памяти
mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd)
if mem_info and mem_info['type'] == 'absolute':
target_addr = mem_info['address']
@ -231,7 +258,7 @@ def encode_lea_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_
return b'\x90' * 3
# ========== 1. Инструкции перемещения данных (MOV, LEA, MOVZX, MOVSX) ==========
# ========== MOV reg, imm (зависит от parse_operand) ==========
def encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None):
"""
@ -291,65 +318,7 @@ def encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, va
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"]
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
# ========== MOVZX/MOVSX (зависят от parse_operand) ==========
def encode_movzx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None):
"""
@ -395,7 +364,7 @@ def encode_movsx(operands, labels, label_sections, symbols, vaddr_text, vaddr_da
return code
# ========== 2. Инструкции сравнения (CMP, TEST) ==========
# ========== CMP reg, imm (зависит от parse_operand) ==========
def encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None):
"""
@ -472,42 +441,7 @@ def encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, va
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) ==========
# ========== ADD/SUB reg, imm (зависит от parse_operand) ==========
def encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None):
"""
@ -573,119 +507,7 @@ def encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols,
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)
code.extend(instr["opcode"])
modrm = 0xC0 | (subop << 3) | 0
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. Инструкции переходов и вызовов ==========
# ========== ПЕРЕХОДЫ И ВЫЗОВЫ (зависят от parse_operand) ==========
def encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos, vaddr_bnd=None):
"""
@ -775,247 +597,6 @@ def encode_loop(operands, labels, label_sections, symbols, vaddr_text, vaddr_dat
return code
# ========== 6. Стековые инструкции ==========
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)
code.append(imm & 0xFF)
else:
code.append(0x68)
code.extend(struct.pack('<i', imm))
return code
# ========== 7. Битовые операции ==========
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)
if size == 64:
code.append(0x48)
code.append(0xC1)
subop_map = {
"сдвиг_влево": 4,
"сдвиг_вправо": 5,
"сдвиг_арифметический_вправо": 7,
"вращать_влево": 0,
"вращать_вправо": 1,
}
subop = subop_map.get(mnemonic, 0)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
code.append(shift & 0xFF)
return bytes(code)
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)
# ========== 8. Системные и отладочные инструкции ==========
def encode_syscall():
return INSTRUCTIONS["вызов_системы"]["opcode"]
def encode_int(operands):
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():
return INSTRUCTIONS["остановить"]["opcode"]
def encode_int3():
return INSTRUCTIONS["отладка"]["opcode"]
def encode_nop():
return INSTRUCTIONS["нет_операции"]["opcode"]
# ========== 9. Инструкции работы с флагами ==========
def encode_flag_instruction(mnemonic):
return INSTRUCTIONS[mnemonic]["opcode"]
# ========== 10. Ввод-вывод ==========
def encode_in(operands):
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):
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):
return INSTRUCTIONS[mnemonic]["opcode"]
# ========== 12. Инструкции идентификации процессора ==========
def encode_cpuid():
return INSTRUCTIONS["идентифицировать_процессор"]["opcode"]
def encode_rdtsc():
return INSTRUCTIONS["прочитать_счётчик"]["opcode"]
# ========== 13. Инструкции с памятью (регистр-регистр) ==========
def encode_xchg(operands):
"""XCHG reg, reg - обменять регистры"""
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, vaddr_bnd=None):

View file

@ -0,0 +1,474 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Кодировщик инструкций фиксированного размера для КВС
Инструкции, не зависящие от сложной адресации памяти.
Вынесены из kvs_pass2_encoder.py для упрощения поддержки.
"""
import struct
from kvs_data import INSTRUCTIONS, REGISTERS, get_reg_info
# ========== MOV регистр-регистр ==========
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"]
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
# ========== MOV reg8, imm8 ==========
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
# ========== CMP/TEST регистр-регистр ==========
def encode_cmp_reg_reg(mnemonic, operands):
"""
CMP reg, reg - сравнить регистры
Формат: сравнить <регистр1>, <регистр2>
Также TEST через проверить.
"""
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
# ========== ADD/SUB регистр-регистр ==========
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
# ========== MUL/IMUL/DIV/IDIV ==========
def encode_muldiv(mnemonic):
"""
MUL/IMUL/DIV/IDIV - умножение и деление (работают с RAX/RDX)
Формат: умножить / умножить_знаковое / разделить / разделить_знаковое
"""
code = bytearray()
instr = INSTRUCTIONS[mnemonic]
subop = instr["subop"]
code.append(0x48)
code.extend(instr["opcode"])
modrm = 0xC0 | (subop << 3) | 0
code.append(modrm)
return code
# ========== AND/OR/XOR регистр-регистр ==========
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
# ========== NOT/NEG ==========
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
# ========== INC/DEC ==========
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)
# ========== Стек (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)
code.append(imm & 0xFF)
else:
code.append(0x68)
code.extend(struct.pack('<i', imm))
return code
# ========== Сдвиги и вращения ==========
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)
if size == 64:
code.append(0x48)
code.append(0xC1)
subop_map = {
"сдвиг_влево": 4,
"сдвиг_вправо": 5,
"сдвиг_арифметический_вправо": 7,
"вращать_влево": 0,
"вращать_вправо": 1,
}
subop = subop_map.get(mnemonic, 0)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
code.append(shift & 0xFF)
return bytes(code)
# ========== Системные и отладочные ==========
def encode_syscall():
return INSTRUCTIONS["вызов_системы"]["opcode"]
def encode_int(operands):
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():
return INSTRUCTIONS["остановить"]["opcode"]
def encode_int3():
return INSTRUCTIONS["отладка"]["opcode"]
def encode_nop():
return INSTRUCTIONS["нет_операции"]["opcode"]
# ========== Инструкции работы с флагами ==========
def encode_flag_instruction(mnemonic):
return INSTRUCTIONS[mnemonic]["opcode"]
# ========== Ввод-вывод ==========
def encode_in(operands):
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):
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_string_instruction(mnemonic):
return INSTRUCTIONS[mnemonic]["opcode"]
# ========== Идентификация процессора ==========
def encode_cpuid():
return INSTRUCTIONS["идентифицировать_процессор"]["opcode"]
def encode_rdtsc():
return INSTRUCTIONS["прочитать_счётчик"]["opcode"]
# ========== XCHG ==========
def encode_xchg(operands):
"""XCHG reg, reg - обменять регистры"""
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

View file

@ -248,4 +248,5 @@ Pass2 (шаг 5):
избавиться от ре и перенести таблицу фиксированных размеров
вынести работу с памятью из второго прохода в отдельный энкодер
вынести работу с памятью из второго прохода в отдельный энкодер
изучить работу с кучей