косвенная адресация
This commit is contained in:
parent
ea01681c81
commit
9ba53693d5
|
|
@ -14,6 +14,33 @@ import sys
|
||||||
sys.path.insert(0, '.')
|
sys.path.insert(0, '.')
|
||||||
from kvs_data import REGISTERS, INSTRUCTIONS
|
from kvs_data import REGISTERS, INSTRUCTIONS
|
||||||
|
|
||||||
|
|
||||||
|
def make_memory_operand(addr_type, base_reg=None, displacement=None, label=None):
|
||||||
|
"""Создаёт кортеж операнда памяти.
|
||||||
|
Формат: (addr_type, base_reg, displacement, label)
|
||||||
|
"""
|
||||||
|
return (addr_type, base_reg, displacement, label)
|
||||||
|
|
||||||
|
|
||||||
|
def memory_operand_to_ast_string(mem_op):
|
||||||
|
"""Преобразует кортеж памяти в строку AST.
|
||||||
|
Формат: 'MEM:тип:данные'
|
||||||
|
"""
|
||||||
|
addr_type = mem_op[0]
|
||||||
|
if addr_type == 'absolute':
|
||||||
|
if mem_op[3] is not None: # label
|
||||||
|
return f"MEM:absolute:{mem_op[3]}"
|
||||||
|
else: # displacement
|
||||||
|
return f"MEM:absolute:{mem_op[2]}"
|
||||||
|
elif addr_type == 'register_indirect':
|
||||||
|
return f"MEM:reg_indirect:{mem_op[1]}"
|
||||||
|
elif addr_type == 'complex':
|
||||||
|
# Пока не поддерживается, но зарезервировано
|
||||||
|
return f"MEM:complex:{mem_op[1]}+{mem_op[2]}*{mem_op[3]}+{mem_op[4]}"
|
||||||
|
else:
|
||||||
|
return f"MEM:unknown"
|
||||||
|
|
||||||
|
|
||||||
def read_tokens(input_file):
|
def read_tokens(input_file):
|
||||||
"""Читает токены из файла"""
|
"""Читает токены из файла"""
|
||||||
tokens = []
|
tokens = []
|
||||||
|
|
@ -30,6 +57,7 @@ def read_tokens(input_file):
|
||||||
tokens.append((tok_type, tok_value))
|
tokens.append((tok_type, tok_value))
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
def __init__(self, tokens):
|
def __init__(self, tokens):
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
|
|
@ -195,7 +223,8 @@ class Parser:
|
||||||
def parse_operand(self):
|
def parse_operand(self):
|
||||||
"""
|
"""
|
||||||
Разбирает один операнд.
|
Разбирает один операнд.
|
||||||
Возвращает строковое представление операнда.
|
Возвращает строку (регистр, число, метка, строка, старый формат памяти)
|
||||||
|
или кортеж (новый формат памяти).
|
||||||
Поддерживает:
|
Поддерживает:
|
||||||
- регистры
|
- регистры
|
||||||
- числа
|
- числа
|
||||||
|
|
@ -208,6 +237,7 @@ class Parser:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if tok[0] == 'LBRACKET':
|
if tok[0] == 'LBRACKET':
|
||||||
|
self.next_token() # съедаем '['
|
||||||
return self.parse_memory_operand()
|
return self.parse_memory_operand()
|
||||||
elif tok[0] == 'NUMBER':
|
elif tok[0] == 'NUMBER':
|
||||||
return self.next_token()[1]
|
return self.next_token()[1]
|
||||||
|
|
@ -220,15 +250,19 @@ class Parser:
|
||||||
|
|
||||||
def parse_memory_operand(self):
|
def parse_memory_operand(self):
|
||||||
"""
|
"""
|
||||||
Разбирает операнд памяти вида:
|
Разбирает операнд памяти.
|
||||||
[регистр]
|
Открывающая скобка уже прочитана в parse_operand.
|
||||||
[число]
|
|
||||||
[регистр + смещение]
|
|
||||||
[регистр + регистр*масштаб]
|
|
||||||
[регистр + регистр*масштаб + смещение]
|
|
||||||
"""
|
|
||||||
self.expect('LBRACKET')
|
|
||||||
|
|
||||||
|
Возвращает:
|
||||||
|
- строку "[выражение]" для абсолютной адресации (старый формат)
|
||||||
|
- кортеж (addr_type, base_reg, displacement, label) для косвенной
|
||||||
|
|
||||||
|
Поддерживает:
|
||||||
|
[регистр] → косвенная адресация (кортеж)
|
||||||
|
[число] → абсолютная (строка)
|
||||||
|
[метка] → абсолютная (строка)
|
||||||
|
[регистр + ...] → пока не поддерживается (строка)
|
||||||
|
"""
|
||||||
parts = []
|
parts = []
|
||||||
|
|
||||||
# Разбираем содержимое до закрывающей скобки
|
# Разбираем содержимое до закрывающей скобки
|
||||||
|
|
@ -246,7 +280,17 @@ class Parser:
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Неожиданный токен в адресации: {tok}")
|
raise ValueError(f"Неожиданный токен в адресации: {tok}")
|
||||||
|
|
||||||
# Собираем выражение
|
# Анализируем собранные части
|
||||||
|
if len(parts) == 1:
|
||||||
|
part = parts[0]
|
||||||
|
# Проверяем, регистр ли это
|
||||||
|
if part in REGISTERS:
|
||||||
|
# Косвенная адресация: [регистр]
|
||||||
|
return make_memory_operand('register_indirect', base_reg=part)
|
||||||
|
# Иначе: [число] или [метка] — возвращаем строку (старый формат)
|
||||||
|
|
||||||
|
# Старое поведение для всего остального
|
||||||
|
# (абсолютная адресация, комплексная - пока не поддерживается)
|
||||||
expr = ' '.join(parts)
|
expr = ' '.join(parts)
|
||||||
return f"[{expr}]"
|
return f"[{expr}]"
|
||||||
|
|
||||||
|
|
@ -264,18 +308,30 @@ class Parser:
|
||||||
continue
|
continue
|
||||||
operands.append(self.parse_operand())
|
operands.append(self.parse_operand())
|
||||||
|
|
||||||
self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(operands))
|
# Преобразуем операнды в строки для AST
|
||||||
|
str_operands = []
|
||||||
|
for op in operands:
|
||||||
|
if isinstance(op, tuple) and len(op) >= 2 and op[0] in ('absolute', 'register_indirect', 'complex'):
|
||||||
|
# Это кортеж памяти
|
||||||
|
str_operands.append(memory_operand_to_ast_string(op))
|
||||||
|
else:
|
||||||
|
# Это обычная строка (регистр, число, метка, старый формат памяти)
|
||||||
|
str_operands.append(op)
|
||||||
|
|
||||||
|
self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(str_operands))
|
||||||
|
|
||||||
def parse_all(self):
|
def parse_all(self):
|
||||||
while self.pos < len(self.tokens):
|
while self.pos < len(self.tokens):
|
||||||
self.parse_line()
|
self.parse_line()
|
||||||
return self.parsed_lines
|
return self.parsed_lines
|
||||||
|
|
||||||
|
|
||||||
def write_ast(ast_lines, output_file):
|
def write_ast(ast_lines, output_file):
|
||||||
with open(output_file, 'w', encoding='utf-8') as f:
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
for line in ast_lines:
|
for line in ast_lines:
|
||||||
f.write(line + '\n')
|
f.write(line + '\n')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) != 3:
|
if len(sys.argv) != 3:
|
||||||
print("Использование: python kvs_parser.py <вход.токены> <выход.аст>")
|
print("Использование: python kvs_parser.py <вход.токены> <выход.аст>")
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,8 @@ class Pass1:
|
||||||
offset_data = align_up(offset_text + text_size, PAGE_SIZE)
|
offset_data = align_up(offset_text + text_size, PAGE_SIZE)
|
||||||
vaddr_text = text_vaddr_base
|
vaddr_text = text_vaddr_base
|
||||||
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE)
|
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE)
|
||||||
vaddr_bnd = align_up(vaddr_data + data_size, PAGE_SIZE)
|
#vaddr_bnd = align_up(vaddr_data + data_size, PAGE_SIZE)
|
||||||
|
vaddr_bnd = align_up(vaddr_data + max(data_size, 1), PAGE_SIZE)
|
||||||
|
|
||||||
comment_size = len("Сборщик КВС".encode('utf-8')) + 1
|
comment_size = len("Сборщик КВС".encode('utf-8')) + 1
|
||||||
offset_comment = align_up(offset_data + data_size, 1)
|
offset_comment = align_up(offset_data + data_size, 1)
|
||||||
|
|
|
||||||
117
kvs_pass2.py
117
kvs_pass2.py
|
|
@ -82,6 +82,98 @@ def read_pass1(input_file):
|
||||||
return data, labels, label_sections, symbols
|
return data, labels, label_sections, symbols
|
||||||
|
|
||||||
|
|
||||||
|
# ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ПАРСИНГА AST ==========
|
||||||
|
|
||||||
|
def parse_ast_line(line):
|
||||||
|
"""
|
||||||
|
Универсальный разбор строки AST.
|
||||||
|
Возвращает (тип, словарь_полей).
|
||||||
|
|
||||||
|
Форматы AST:
|
||||||
|
- DIRECTIVE:имя
|
||||||
|
- DIRECTIVE:имя:секция
|
||||||
|
- DIRECTIVE:имя:секция:данные
|
||||||
|
- DIRECTIVE:имя:имя_константы:значение
|
||||||
|
- LABEL:имя:секция
|
||||||
|
- INSTR:мнемоника:секция:операнд1,операнд2,...
|
||||||
|
|
||||||
|
Операнды могут содержать двоеточия (MEM:reg_indirect:рбикс).
|
||||||
|
Поэтому для INSTR используется специальный разбор: первые 3 двоеточия
|
||||||
|
отделяют служебные поля, остальное — операнды.
|
||||||
|
"""
|
||||||
|
if not line:
|
||||||
|
return None, {}
|
||||||
|
|
||||||
|
line_type = line.split(':')[0]
|
||||||
|
|
||||||
|
if line_type == "INSTR":
|
||||||
|
# Формат: INSTR:мнемоника:секция:операнды
|
||||||
|
# Находим первые три двоеточия
|
||||||
|
first_colon = line.find(':')
|
||||||
|
second_colon = line.find(':', first_colon + 1)
|
||||||
|
third_colon = line.find(':', second_colon + 1)
|
||||||
|
|
||||||
|
if first_colon == -1 or second_colon == -1 or third_colon == -1:
|
||||||
|
return "INSTR", {}
|
||||||
|
|
||||||
|
mnemonic = line[first_colon + 1 : second_colon]
|
||||||
|
section = line[second_colon + 1 : third_colon]
|
||||||
|
operands_str = line[third_colon + 1 :]
|
||||||
|
|
||||||
|
operands = []
|
||||||
|
if operands_str:
|
||||||
|
operands = operands_str.split(',')
|
||||||
|
|
||||||
|
return "INSTR", {
|
||||||
|
'mnemonic': mnemonic,
|
||||||
|
'section': section,
|
||||||
|
'operands': operands,
|
||||||
|
'operands_str': operands_str
|
||||||
|
}
|
||||||
|
|
||||||
|
elif line_type == "DIRECTIVE":
|
||||||
|
parts = line.split(':')
|
||||||
|
directive = parts[1] if len(parts) > 1 else ""
|
||||||
|
|
||||||
|
if directive in ('.текст', '.данные', '.бнд'):
|
||||||
|
return "DIRECTIVE", {'directive': directive}
|
||||||
|
|
||||||
|
elif directive == '.глобал':
|
||||||
|
label = parts[2] if len(parts) > 2 else ""
|
||||||
|
return "DIRECTIVE", {'directive': directive, 'label': label}
|
||||||
|
|
||||||
|
elif directive in ('.строка_нуль', '.строка'):
|
||||||
|
sec = parts[2] if len(parts) > 2 else ""
|
||||||
|
s = parts[3] if len(parts) > 3 else ""
|
||||||
|
return "DIRECTIVE", {'directive': directive, 'section': sec, 'string': s}
|
||||||
|
|
||||||
|
elif directive == '.константа':
|
||||||
|
name = parts[2] if len(parts) > 2 else ""
|
||||||
|
value = parts[3] if len(parts) > 3 else ""
|
||||||
|
return "DIRECTIVE", {'directive': directive, 'name': name, 'value': value}
|
||||||
|
|
||||||
|
elif directive == '.байт':
|
||||||
|
sec = parts[2] if len(parts) > 2 else ""
|
||||||
|
bytes_str = parts[3] if len(parts) > 3 else ""
|
||||||
|
return "DIRECTIVE", {'directive': directive, 'section': sec, 'bytes': bytes_str}
|
||||||
|
|
||||||
|
elif directive in ('.резб', '.резс', '.рездс', '.резкс'):
|
||||||
|
sec = parts[2] if len(parts) > 2 else ""
|
||||||
|
count = parts[3] if len(parts) > 3 else "0"
|
||||||
|
return "DIRECTIVE", {'directive': directive, 'section': sec, 'count': count}
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "DIRECTIVE", {'directive': directive}
|
||||||
|
|
||||||
|
elif line_type == "LABEL":
|
||||||
|
parts = line.split(':')
|
||||||
|
label_name = parts[1] if len(parts) > 1 else ""
|
||||||
|
sec = parts[2] if len(parts) > 2 else ""
|
||||||
|
return "LABEL", {'name': label_name, 'section': sec}
|
||||||
|
|
||||||
|
return line_type, {}
|
||||||
|
|
||||||
|
|
||||||
# ========== ОСНОВНОЙ КЛАСС ВТОРОГО ПРОХОДА ==========
|
# ========== ОСНОВНОЙ КЛАСС ВТОРОГО ПРОХОДА ==========
|
||||||
|
|
||||||
class Pass2:
|
class Pass2:
|
||||||
|
|
@ -130,17 +222,17 @@ class Pass2:
|
||||||
|
|
||||||
def process_line(self, line):
|
def process_line(self, line):
|
||||||
"""Обрабатывает одну строку AST"""
|
"""Обрабатывает одну строку AST"""
|
||||||
parts = line.split(':')
|
line_type, fields = parse_ast_line(line)
|
||||||
line_type = parts[0]
|
|
||||||
|
|
||||||
if line_type == "DIRECTIVE":
|
if line_type == "DIRECTIVE":
|
||||||
directive = parts[1]
|
directive = fields.get('directive', '')
|
||||||
|
|
||||||
if directive == '.текст':
|
if directive == '.текст':
|
||||||
self.current_section = ".text"
|
self.current_section = ".text"
|
||||||
elif directive == '.данные':
|
elif directive == '.данные':
|
||||||
self.current_section = ".data"
|
self.current_section = ".data"
|
||||||
elif directive in ('.строка_нуль', '.строка'):
|
elif directive in ('.строка_нуль', '.строка'):
|
||||||
s = parts[3] if len(parts) > 3 else ""
|
s = fields.get('string', '')
|
||||||
real_s = unescape_string(s)
|
real_s = unescape_string(s)
|
||||||
bstring = real_s.encode('utf-8')
|
bstring = real_s.encode('utf-8')
|
||||||
if directive == '.строка_нуль':
|
if directive == '.строка_нуль':
|
||||||
|
|
@ -159,8 +251,8 @@ class Pass2:
|
||||||
self.position[".data"] += len(bstring)
|
self.position[".data"] += len(bstring)
|
||||||
|
|
||||||
elif directive == '.байт':
|
elif directive == '.байт':
|
||||||
if len(parts) > 3 and parts[3]:
|
bytes_str = fields.get('bytes', '')
|
||||||
bytes_str = parts[3]
|
if bytes_str:
|
||||||
byte_values = bytes_str.split(',')
|
byte_values = bytes_str.split(',')
|
||||||
original_cmd = f".байт {bytes_str}"
|
original_cmd = f".байт {bytes_str}"
|
||||||
start_addr = self.vaddr_data + self.position[".data"]
|
start_addr = self.vaddr_data + self.position[".data"]
|
||||||
|
|
@ -182,18 +274,15 @@ class Pass2:
|
||||||
self.position[".data"] += len(byte_values)
|
self.position[".data"] += len(byte_values)
|
||||||
|
|
||||||
elif line_type == "LABEL":
|
elif line_type == "LABEL":
|
||||||
label_name = parts[1]
|
label_name = fields.get('name', '')
|
||||||
sec = parts[2]
|
sec = fields.get('section', '')
|
||||||
if label_name in self.labels:
|
if label_name in self.labels:
|
||||||
self.position[sec] = self.labels[label_name]
|
self.position[sec] = self.labels[label_name]
|
||||||
|
|
||||||
elif line_type == "INSTR":
|
elif line_type == "INSTR":
|
||||||
mnemonic = parts[1]
|
mnemonic = fields.get('mnemonic', '')
|
||||||
sec = parts[2]
|
sec = fields.get('section', '')
|
||||||
|
operands = fields.get('operands', [])
|
||||||
operands = []
|
|
||||||
if len(parts) > 3 and parts[3]:
|
|
||||||
operands = parts[3].split(',')
|
|
||||||
|
|
||||||
if operands:
|
if operands:
|
||||||
original_cmd = f"{mnemonic} {', '.join(operands)}"
|
original_cmd = f"{mnemonic} {', '.join(operands)}"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,23 @@
|
||||||
Автономный модуль — не импортирует из kvs_pass2_encoder_fixsize.
|
Автономный модуль — не импортирует из kvs_pass2_encoder_fixsize.
|
||||||
Содержит parse_operand, parse_memory_operand, инструкции с адресацией, диспетчер.
|
Содержит parse_operand, parse_memory_operand, инструкции с адресацией, диспетчер.
|
||||||
Дублируемые с fixsize функции помечены комментарием # ДУБЛИКАТ.
|
Дублируемые с fixsize функции помечены комментарием # ДУБЛИКАТ.
|
||||||
|
|
||||||
|
ФОРМАТ ВХОДНЫХ ДАННЫХ:
|
||||||
|
- operands: список строк, по одной на операнд
|
||||||
|
- Для косвенной адресации: 'MEM:reg_indirect:имя_регистра' (например 'MEM:reg_indirect:рбикс')
|
||||||
|
- Для абсолютной адресации: '[число]' или '[метка]' (старый формат)
|
||||||
|
- Для регистров: имя регистра (например 'раикс')
|
||||||
|
- Для непосредственных значений: число или метка
|
||||||
|
|
||||||
|
СТРУКТУРА REGISTERS (из kvs_data.py):
|
||||||
|
- Ключ: имя регистра (строка, например "раикс", "рбикс")
|
||||||
|
- Значение: индекс регистра (целое число 0-15)
|
||||||
|
- Пример: REGISTERS["раикс"] = 0, REGISTERS["рбикс"] = 3
|
||||||
|
|
||||||
|
СТРУКТУРА get_reg_info(reg_name):
|
||||||
|
- Возвращает словарь: {"size": 64, "index": 0} для 64-битных
|
||||||
|
- "index" — индекс регистра (0-15), используется в ModR/M и REX
|
||||||
|
- "size" — размер в битах (8, 16, 32, 64)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
|
|
@ -53,11 +70,56 @@ def parse_memory_operand(operand_str, labels, label_sections, vaddr_text, vaddr_
|
||||||
"""
|
"""
|
||||||
Разбирает операнд памяти.
|
Разбирает операнд памяти.
|
||||||
Возвращает словарь с информацией или None.
|
Возвращает словарь с информацией или None.
|
||||||
Поддерживает:
|
|
||||||
|
Поддерживаемые форматы:
|
||||||
- [число] — абсолютный адрес
|
- [число] — абсолютный адрес
|
||||||
- [метка] — адрес метки (из .data, .text или .бнд)
|
- [метка] — адрес метки (из .data, .text или .бнд)
|
||||||
- [регистр] — косвенная адресация (TODO)
|
- MEM:reg_indirect:регистр — косвенная адресация (новый формат)
|
||||||
|
- MEM:absolute:число — абсолютный адрес (новый формат, для будущего использования)
|
||||||
|
- MEM:absolute:метка — абсолютный адрес по метке (новый формат, для будущего использования)
|
||||||
|
|
||||||
|
Возвращаемый словарь:
|
||||||
|
- Для 'absolute': {'type': 'absolute', 'address': число}
|
||||||
|
- Для 'register_indirect': {'type': 'register_indirect', 'base_reg': имя_регистра}
|
||||||
"""
|
"""
|
||||||
|
# --- Новый формат: MEM:тип:данные ---
|
||||||
|
if operand_str.startswith('MEM:'):
|
||||||
|
parts = operand_str.split(':')
|
||||||
|
if len(parts) >= 3:
|
||||||
|
addr_type = parts[1]
|
||||||
|
|
||||||
|
if addr_type == 'reg_indirect':
|
||||||
|
# MEM:reg_indirect:имя_регистра
|
||||||
|
base_reg = parts[2]
|
||||||
|
if base_reg in REGISTERS:
|
||||||
|
return {'type': 'register_indirect', 'base_reg': base_reg}
|
||||||
|
|
||||||
|
elif addr_type == 'absolute':
|
||||||
|
# MEM:absolute:число или MEM:absolute:метка
|
||||||
|
value = parts[2]
|
||||||
|
if value.isdigit():
|
||||||
|
return {'type': 'absolute', 'address': int(value)}
|
||||||
|
elif value.startswith('0x') or value.startswith('0X'):
|
||||||
|
try:
|
||||||
|
return {'type': 'absolute', 'address': int(value, 16)}
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
elif value in labels:
|
||||||
|
section = label_sections.get(value, '.data')
|
||||||
|
if section == '.text':
|
||||||
|
base = vaddr_text
|
||||||
|
elif section == '.data':
|
||||||
|
base = vaddr_data
|
||||||
|
elif section == '.бнд':
|
||||||
|
base = vaddr_bnd if vaddr_bnd is not None else vaddr_data
|
||||||
|
else:
|
||||||
|
base = vaddr_text
|
||||||
|
addr = labels[value] + base
|
||||||
|
return {'type': 'absolute', 'address': addr}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# --- Старый формат: [число] или [метка] ---
|
||||||
if not operand_str.startswith('[') or not operand_str.endswith(']'):
|
if not operand_str.startswith('[') or not operand_str.endswith(']'):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -127,13 +189,91 @@ def encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text):
|
||||||
return code
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def encode_mov_reg_mem_indirect(reg_info, base_reg):
|
||||||
|
"""
|
||||||
|
Кодирует MOV reg, [base_reg] — косвенная адресация.
|
||||||
|
Пример: mov rax, [rbx] → 48 8B 03
|
||||||
|
|
||||||
|
Аргументы:
|
||||||
|
- reg_info: словарь от get_reg_info() с ключами 'index' (int) и 'size' (int)
|
||||||
|
- base_reg: строка с именем регистра, например 'рбикс'
|
||||||
|
|
||||||
|
REGISTERS[base_reg] возвращает число-индекс (int), НЕ словарь.
|
||||||
|
"""
|
||||||
|
code = bytearray()
|
||||||
|
reg = reg_info['index']
|
||||||
|
base = REGISTERS[base_reg] # REGISTERS возвращает int (индекс регистра)
|
||||||
|
|
||||||
|
rex = 0x48 if reg_info['size'] == 64 else 0x40
|
||||||
|
if reg >= 8:
|
||||||
|
rex |= 0x04 # REX.R
|
||||||
|
if base >= 8:
|
||||||
|
rex |= 0x01 # REX.B
|
||||||
|
code.append(rex)
|
||||||
|
code.append(0x8B) # MOV r64, r/m64
|
||||||
|
|
||||||
|
# Особый случай: [rbp] или [r13] требует disp8=0 при mod=00
|
||||||
|
# (mod=00 с r/m=101 означает RIP-relative, а не [rbp])
|
||||||
|
if (base & 7) == 5:
|
||||||
|
# mod=01 (disp8), reg, r/m=5
|
||||||
|
modrm = 0x40 | ((reg & 7) << 3) | 5
|
||||||
|
code.append(modrm)
|
||||||
|
code.append(0x00) # disp8 = 0
|
||||||
|
else:
|
||||||
|
# mod=00, reg, r/m=base
|
||||||
|
modrm = ((reg & 7) << 3) | (base & 7)
|
||||||
|
code.append(modrm)
|
||||||
|
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def encode_mov_mem_reg_indirect(base_reg, reg_info):
|
||||||
|
"""
|
||||||
|
Кодирует MOV [base_reg], reg — косвенная адресация.
|
||||||
|
Пример: mov [rsi], rax → 48 89 06
|
||||||
|
|
||||||
|
Аргументы:
|
||||||
|
- base_reg: строка с именем регистра, например 'рсикс'
|
||||||
|
- reg_info: словарь от get_reg_info() с ключами 'index' (int) и 'size' (int)
|
||||||
|
|
||||||
|
REGISTERS[base_reg] возвращает число-индекс (int), НЕ словарь.
|
||||||
|
"""
|
||||||
|
code = bytearray()
|
||||||
|
reg = reg_info['index']
|
||||||
|
base = REGISTERS[base_reg] # REGISTERS возвращает int (индекс регистра)
|
||||||
|
|
||||||
|
rex = 0x48 if reg_info['size'] == 64 else 0x40
|
||||||
|
if reg >= 8:
|
||||||
|
rex |= 0x04 # REX.R
|
||||||
|
if base >= 8:
|
||||||
|
rex |= 0x01 # REX.B
|
||||||
|
code.append(rex)
|
||||||
|
code.append(0x89) # MOV r/m64, r64
|
||||||
|
|
||||||
|
# Особый случай: [rbp] или [r13] требует disp8=0 при mod=00
|
||||||
|
if (base & 7) == 5:
|
||||||
|
modrm = 0x40 | ((reg & 7) << 3) | 5
|
||||||
|
code.append(modrm)
|
||||||
|
code.append(0x00)
|
||||||
|
else:
|
||||||
|
modrm = ((reg & 7) << 3) | (base & 7)
|
||||||
|
code.append(modrm)
|
||||||
|
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
def encode_mov_reg_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos, vaddr_bnd=None):
|
def encode_mov_reg_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos, vaddr_bnd=None):
|
||||||
"""MOV reg, mem — загрузить"""
|
"""MOV reg, mem — загрузить"""
|
||||||
reg_info = get_reg_info(operands[0])
|
reg_info = get_reg_info(operands[0])
|
||||||
mem_operand = operands[1]
|
mem_operand = operands[1]
|
||||||
mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd)
|
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)
|
if mem_info:
|
||||||
|
if mem_info['type'] == 'absolute':
|
||||||
|
return encode_mov_reg_mem_absolute(reg_info, mem_info, current_pos, vaddr_text)
|
||||||
|
elif mem_info['type'] == 'register_indirect':
|
||||||
|
return encode_mov_reg_mem_indirect(reg_info, mem_info['base_reg'])
|
||||||
|
|
||||||
print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr)
|
print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr)
|
||||||
return b'\x90' * 3
|
return b'\x90' * 3
|
||||||
|
|
||||||
|
|
@ -143,8 +283,13 @@ def encode_mov_mem_reg(operands, labels, label_sections, symbols, vaddr_text, va
|
||||||
mem_operand = operands[0]
|
mem_operand = operands[0]
|
||||||
reg_info = get_reg_info(operands[1])
|
reg_info = get_reg_info(operands[1])
|
||||||
mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd)
|
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)
|
if mem_info:
|
||||||
|
if mem_info['type'] == 'absolute':
|
||||||
|
return encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text)
|
||||||
|
elif mem_info['type'] == 'register_indirect':
|
||||||
|
return encode_mov_mem_reg_indirect(mem_info['base_reg'], reg_info)
|
||||||
|
|
||||||
print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr)
|
print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr)
|
||||||
return b'\x90' * 3
|
return b'\x90' * 3
|
||||||
|
|
||||||
|
|
@ -871,6 +1016,9 @@ def encode_instruction(mnemonic, operands, labels, label_sections, symbols, vadd
|
||||||
"""
|
"""
|
||||||
Главный диспетчер генерации машинного кода.
|
Главный диспетчер генерации машинного кода.
|
||||||
По мнемонике выбирает соответствующую функцию генерации.
|
По мнемонике выбирает соответствующую функцию генерации.
|
||||||
|
|
||||||
|
Аргументы:
|
||||||
|
- operands: список строк с операндами (например ['раикс', 'MEM:reg_indirect:рбикс'])
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# ----- Инструкции без операндов -----
|
# ----- Инструкции без операндов -----
|
||||||
|
|
|
||||||
91
тесты/bss_bug.txt
Normal file
91
тесты/bss_bug.txt
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
## Исходник
|
||||||
|
|
||||||
|
```asm
|
||||||
|
.текст
|
||||||
|
.глобал _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
; ТЕСТ 1: Абсолютная адресация в .data
|
||||||
|
переместить_имм раикс, 0x42
|
||||||
|
сохранить [абс_данные], раикс ; запись 0x42 по адресу абс_данные (0x402000)
|
||||||
|
|
||||||
|
; ТЕСТ 2: Косвенная адресация в .data
|
||||||
|
переместить_имм рбикс, косв_данные ; рбикс = адрес косв_данные (0x402001)
|
||||||
|
переместить_имм раикс, 0x99
|
||||||
|
сохранить [рбикс], раикс ; запись 0x99 по адресу 0x402001
|
||||||
|
|
||||||
|
; ТЕСТ 3: Косвенная адресация в .бнд
|
||||||
|
переместить_имм рсикс, неиниц_бнд ; рсикс = адрес неиниц_бнд
|
||||||
|
переместить_имм раикс, 0x77
|
||||||
|
сохранить [рсикс], раикс ; запись 0x77 по адресу неиниц_бнд
|
||||||
|
|
||||||
|
; Читаем всё обратно
|
||||||
|
загрузить р8, [абс_данные] ; чтение из 0x402000
|
||||||
|
загрузить р9, [рбикс] ; чтение из 0x402001
|
||||||
|
загрузить р10, [рсикс] ; чтение из неиниц_бнд
|
||||||
|
|
||||||
|
; ЗАВЕРШЕНИЕ: возвращаем значение из абс_данные
|
||||||
|
загрузить рдиай, [абс_данные] ; рдиай = значение из 0x402000
|
||||||
|
переместить_имм раикс, 60 ; номер системного вызова exit
|
||||||
|
вызов_системы
|
||||||
|
|
||||||
|
.данные
|
||||||
|
абс_данные:
|
||||||
|
.байт 0
|
||||||
|
косв_данные:
|
||||||
|
.байт 0
|
||||||
|
|
||||||
|
.бнд
|
||||||
|
неиниц_бнд:
|
||||||
|
.резб 8
|
||||||
|
```
|
||||||
|
|
||||||
|
## Размещение секций в памяти
|
||||||
|
|
||||||
|
```
|
||||||
|
0x401000 ─ .text (код)
|
||||||
|
0x402000 ─ .data
|
||||||
|
├── абс_данные (смещение 0): байт 0
|
||||||
|
└── косв_данные (смещение 1): байт 0
|
||||||
|
0x403000 ─ .бнд
|
||||||
|
└── неиниц_бнд (смещение 0): 8 байт, ноль
|
||||||
|
```
|
||||||
|
|
||||||
|
## Что подразумевается
|
||||||
|
|
||||||
|
1. **Тест 1** пишет `0x42` в `абс_данные` по адресу `0x402000`.
|
||||||
|
2. **Тест 2** пишет `0x99` в `косв_данные` по адресу `0x402001`.
|
||||||
|
3. **Тест 3** пишет `0x77` в `неиниц_бнд` по адресу **`0x403000`**.
|
||||||
|
|
||||||
|
После этого:
|
||||||
|
- `[0x402000]` содержит `0x42`
|
||||||
|
- `[0x402001]` содержит `0x99`
|
||||||
|
- `[0x403000]` содержит `0x77`
|
||||||
|
|
||||||
|
Финал читает `[абс_данные]` = `[0x402000]` = `0x42` в `рдиай` и завершается.
|
||||||
|
|
||||||
|
**Ожидаемый код возврата:** `66` (это `0x42`).
|
||||||
|
|
||||||
|
## Что происходит на самом деле
|
||||||
|
|
||||||
|
**Фактический код возврата:** `119` (это `0x77`).
|
||||||
|
|
||||||
|
## Причина
|
||||||
|
|
||||||
|
Инструкция `переместить_имм рсикс, неиниц_бнд` загружает в `рсикс` **неправильный адрес**. Вместо `0x403000` (адрес `.бнд`) регистр получает `0x402002` (адрес внутри `.data`).
|
||||||
|
|
||||||
|
Это происходит потому, что при вычислении адреса метки `неиниц_бнд` компилятор ошибочно использует базовый адрес секции `.data` (`0x402000`) вместо базового адреса секции `.бнд` (`0x403000`).
|
||||||
|
|
||||||
|
Смещение метки `неиниц_бнд` внутри `.бнд` равно `0`. Правильный расчёт должен быть: `0x403000 + 0 = 0x403000`. Но компилятор считает: `0x402000 + 0 = 0x402000`.
|
||||||
|
|
||||||
|
## Цепочка последствий
|
||||||
|
|
||||||
|
1. `рсикс` получает `0x402000` вместо `0x403000`.
|
||||||
|
2. `сохранить [рсикс], раикс` пишет `0x77` по адресу `0x402000` — **это ячейка `абс_данные`**.
|
||||||
|
3. Значение `0x42`, записанное в Тесте 1, **затирается** значением `0x77`.
|
||||||
|
4. Финал читает `[абс_данные]` и получает `0x77` вместо `0x42`.
|
||||||
|
5. Код возврата становится `119` вместо `66`.
|
||||||
|
|
||||||
|
## Как проверить
|
||||||
|
|
||||||
|
Если закомментировать Тест 3 (строки с `рсикс` и `неиниц_бнд`), код возврата станет `66` — баг исчезает, потому что `0x402000` не перезаписывается.
|
||||||
53
тесты/bss_bug.квс
Normal file
53
тесты/bss_bug.квс
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
.текст
|
||||||
|
.глобал _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
; =============================================
|
||||||
|
; ТЕСТ 1: Абсолютная адресация в .data
|
||||||
|
; =============================================
|
||||||
|
переместить_имм раикс, 0x42
|
||||||
|
сохранить [абс_данные], раикс
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; ТЕСТ 2: Косвенная адресация в .data
|
||||||
|
; =============================================
|
||||||
|
переместить_имм рбикс, косв_данные
|
||||||
|
переместить_имм раикс, 0x99
|
||||||
|
сохранить [рбикс], раикс
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; ТЕСТ 3: Косвенная адресация в .бнд
|
||||||
|
; =============================================
|
||||||
|
переместить_имм рсикс, неиниц_бнд
|
||||||
|
переместить_имм раикс, 0x77
|
||||||
|
сохранить [рсикс], раикс
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; Читаем всё обратно и проверяем
|
||||||
|
; =============================================
|
||||||
|
|
||||||
|
; Читаем абсолютную из .data
|
||||||
|
загрузить р8, [абс_данные]
|
||||||
|
|
||||||
|
; Читаем косвенную из .data
|
||||||
|
загрузить р9, [рбикс]
|
||||||
|
|
||||||
|
; Читаем косвенную из .бнд
|
||||||
|
загрузить р10, [рсикс]
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; ЗАВЕРШЕНИЕ: возвращаем значение из абсолютной .data
|
||||||
|
; =============================================
|
||||||
|
загрузить рдиай, [абс_данные]
|
||||||
|
переместить_имм раикс, 60
|
||||||
|
вызов_системы
|
||||||
|
|
||||||
|
.данные
|
||||||
|
абс_данные:
|
||||||
|
.байт 0
|
||||||
|
косв_данные:
|
||||||
|
.байт 0
|
||||||
|
|
||||||
|
.бнд
|
||||||
|
неиниц_бнд:
|
||||||
|
.резб 8
|
||||||
34
тесты/test_minimal2.квс
Normal file
34
тесты/test_minimal2.квс
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
.текст
|
||||||
|
.глобал _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
; === ТЕСТ 1: Запись через косвенную адресацию ===
|
||||||
|
; Загружаем адрес переменной в регистр
|
||||||
|
переместить_имм рбикс, тестовая_переменная
|
||||||
|
|
||||||
|
; Записываем 42 через косвенную адресацию [рбикс]
|
||||||
|
переместить_имм раикс, 42
|
||||||
|
сохранить [рбикс], раикс
|
||||||
|
|
||||||
|
; === ТЕСТ 2: Чтение через косвенную адресацию ===
|
||||||
|
; Загружаем адрес другой переменной в регистр
|
||||||
|
переместить_имм рсикс, вторая_переменная
|
||||||
|
|
||||||
|
; Читаем через косвенную адресацию [рсикс]
|
||||||
|
загрузить рдикс, [рсикс]
|
||||||
|
|
||||||
|
; === ТЕСТ 3: Проверяем, что записалось ===
|
||||||
|
; Читаем первую переменную через косвенную
|
||||||
|
загрузить рсикс, [рбикс]
|
||||||
|
|
||||||
|
; === ЗАВЕРШЕНИЕ ===
|
||||||
|
; Выходим с кодом из первой переменной (должно быть 42)
|
||||||
|
переместить_имм раикс, 60
|
||||||
|
переместить рдиай, рсикс
|
||||||
|
вызов_системы
|
||||||
|
|
||||||
|
.данные
|
||||||
|
тестовая_переменная:
|
||||||
|
.байт 0
|
||||||
|
вторая_переменная:
|
||||||
|
.байт 99
|
||||||
50
тесты/test_minimal3.квс
Normal file
50
тесты/test_minimal3.квс
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
.текст
|
||||||
|
.глобал _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
; =============================================
|
||||||
|
; ТЕСТ 1: Абсолютная адресация
|
||||||
|
; =============================================
|
||||||
|
переместить_имм раикс, 0x42
|
||||||
|
сохранить [абс_данные], раикс
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; ТЕСТ 2: Косвенная адресация
|
||||||
|
; =============================================
|
||||||
|
переместить_имм рбикс, косв_данные
|
||||||
|
переместить_имм раикс, 0x99
|
||||||
|
сохранить [рбикс], раикс
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; ТЕСТ 3: Ещё одна косвенная адресация
|
||||||
|
; =============================================
|
||||||
|
переместить_имм рсикс, ещё_данные
|
||||||
|
переместить_имм раикс, 0x77
|
||||||
|
сохранить [рсикс], раикс
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; Читаем всё обратно
|
||||||
|
; =============================================
|
||||||
|
загрузить р8, [абс_данные]
|
||||||
|
загрузить р9, [рбикс]
|
||||||
|
загрузить р10, [рсикс]
|
||||||
|
|
||||||
|
; =============================================
|
||||||
|
; ЗАВЕРШЕНИЕ: возвращаем из абсолютной переменной
|
||||||
|
; =============================================
|
||||||
|
загрузить рдиай, [абс_данные]
|
||||||
|
переместить_имм раикс, 60
|
||||||
|
вызов_системы
|
||||||
|
|
||||||
|
.данные
|
||||||
|
абс_данные:
|
||||||
|
.байт 0
|
||||||
|
косв_данные:
|
||||||
|
.байт 0
|
||||||
|
ещё_данные:
|
||||||
|
.байт 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
|
||||||
|
.бнд
|
||||||
|
буфер: .резб 1048576 ; 1 МБ = 1024 * 1024
|
||||||
|
переменная: .резкс 1 ; 8 байт под длину
|
||||||
|
буфер_строки: .резкс 1 ; 8 байт под указатель на строку
|
||||||
Loading…
Reference in a new issue