косвенная адресация
This commit is contained in:
parent
ea01681c81
commit
9ba53693d5
|
|
@ -14,6 +14,33 @@ import sys
|
|||
sys.path.insert(0, '.')
|
||||
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):
|
||||
"""Читает токены из файла"""
|
||||
tokens = []
|
||||
|
|
@ -30,6 +57,7 @@ def read_tokens(input_file):
|
|||
tokens.append((tok_type, tok_value))
|
||||
return tokens
|
||||
|
||||
|
||||
class Parser:
|
||||
def __init__(self, tokens):
|
||||
self.tokens = tokens
|
||||
|
|
@ -195,7 +223,8 @@ class Parser:
|
|||
def parse_operand(self):
|
||||
"""
|
||||
Разбирает один операнд.
|
||||
Возвращает строковое представление операнда.
|
||||
Возвращает строку (регистр, число, метка, строка, старый формат памяти)
|
||||
или кортеж (новый формат памяти).
|
||||
Поддерживает:
|
||||
- регистры
|
||||
- числа
|
||||
|
|
@ -208,6 +237,7 @@ class Parser:
|
|||
return None
|
||||
|
||||
if tok[0] == 'LBRACKET':
|
||||
self.next_token() # съедаем '['
|
||||
return self.parse_memory_operand()
|
||||
elif tok[0] == 'NUMBER':
|
||||
return self.next_token()[1]
|
||||
|
|
@ -220,15 +250,19 @@ class Parser:
|
|||
|
||||
def parse_memory_operand(self):
|
||||
"""
|
||||
Разбирает операнд памяти вида:
|
||||
[регистр]
|
||||
[число]
|
||||
[регистр + смещение]
|
||||
[регистр + регистр*масштаб]
|
||||
[регистр + регистр*масштаб + смещение]
|
||||
"""
|
||||
self.expect('LBRACKET')
|
||||
Разбирает операнд памяти.
|
||||
Открывающая скобка уже прочитана в parse_operand.
|
||||
|
||||
Возвращает:
|
||||
- строку "[выражение]" для абсолютной адресации (старый формат)
|
||||
- кортеж (addr_type, base_reg, displacement, label) для косвенной
|
||||
|
||||
Поддерживает:
|
||||
[регистр] → косвенная адресация (кортеж)
|
||||
[число] → абсолютная (строка)
|
||||
[метка] → абсолютная (строка)
|
||||
[регистр + ...] → пока не поддерживается (строка)
|
||||
"""
|
||||
parts = []
|
||||
|
||||
# Разбираем содержимое до закрывающей скобки
|
||||
|
|
@ -246,7 +280,17 @@ class Parser:
|
|||
else:
|
||||
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)
|
||||
return f"[{expr}]"
|
||||
|
||||
|
|
@ -264,18 +308,30 @@ class Parser:
|
|||
continue
|
||||
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):
|
||||
while self.pos < len(self.tokens):
|
||||
self.parse_line()
|
||||
return self.parsed_lines
|
||||
|
||||
|
||||
def write_ast(ast_lines, output_file):
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
for line in ast_lines:
|
||||
f.write(line + '\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
print("Использование: python kvs_parser.py <вход.токены> <выход.аст>")
|
||||
|
|
|
|||
|
|
@ -316,7 +316,8 @@ class Pass1:
|
|||
offset_data = align_up(offset_text + text_size, PAGE_SIZE)
|
||||
vaddr_text = text_vaddr_base
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
# ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ПАРСИНГА 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:
|
||||
|
|
@ -130,17 +222,17 @@ class Pass2:
|
|||
|
||||
def process_line(self, line):
|
||||
"""Обрабатывает одну строку AST"""
|
||||
parts = line.split(':')
|
||||
line_type = parts[0]
|
||||
line_type, fields = parse_ast_line(line)
|
||||
|
||||
if line_type == "DIRECTIVE":
|
||||
directive = parts[1]
|
||||
directive = fields.get('directive', '')
|
||||
|
||||
if directive == '.текст':
|
||||
self.current_section = ".text"
|
||||
elif directive == '.данные':
|
||||
self.current_section = ".data"
|
||||
elif directive in ('.строка_нуль', '.строка'):
|
||||
s = parts[3] if len(parts) > 3 else ""
|
||||
s = fields.get('string', '')
|
||||
real_s = unescape_string(s)
|
||||
bstring = real_s.encode('utf-8')
|
||||
if directive == '.строка_нуль':
|
||||
|
|
@ -159,8 +251,8 @@ class Pass2:
|
|||
self.position[".data"] += len(bstring)
|
||||
|
||||
elif directive == '.байт':
|
||||
if len(parts) > 3 and parts[3]:
|
||||
bytes_str = parts[3]
|
||||
bytes_str = fields.get('bytes', '')
|
||||
if bytes_str:
|
||||
byte_values = bytes_str.split(',')
|
||||
original_cmd = f".байт {bytes_str}"
|
||||
start_addr = self.vaddr_data + self.position[".data"]
|
||||
|
|
@ -182,18 +274,15 @@ class Pass2:
|
|||
self.position[".data"] += len(byte_values)
|
||||
|
||||
elif line_type == "LABEL":
|
||||
label_name = parts[1]
|
||||
sec = parts[2]
|
||||
label_name = fields.get('name', '')
|
||||
sec = fields.get('section', '')
|
||||
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(',')
|
||||
mnemonic = fields.get('mnemonic', '')
|
||||
sec = fields.get('section', '')
|
||||
operands = fields.get('operands', [])
|
||||
|
||||
if operands:
|
||||
original_cmd = f"{mnemonic} {', '.join(operands)}"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,23 @@
|
|||
Автономный модуль — не импортирует из kvs_pass2_encoder_fixsize.
|
||||
Содержит parse_operand, parse_memory_operand, инструкции с адресацией, диспетчер.
|
||||
Дублируемые с 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
|
||||
|
|
@ -53,11 +70,56 @@ def parse_memory_operand(operand_str, labels, label_sections, vaddr_text, vaddr_
|
|||
"""
|
||||
Разбирает операнд памяти.
|
||||
Возвращает словарь с информацией или None.
|
||||
Поддерживает:
|
||||
|
||||
Поддерживаемые форматы:
|
||||
- [число] — абсолютный адрес
|
||||
- [метка] — адрес метки (из .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(']'):
|
||||
return None
|
||||
|
||||
|
|
@ -127,13 +189,91 @@ def encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text):
|
|||
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):
|
||||
"""MOV reg, mem — загрузить"""
|
||||
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)
|
||||
|
||||
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)
|
||||
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]
|
||||
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)
|
||||
|
||||
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)
|
||||
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