From 9ba53693d5c2c5edcf3c15632a38ff0eaad075ad Mon Sep 17 00:00:00 2001 From: mzhoot Date: Sun, 17 May 2026 15:18:35 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BA=D0=BE=D1=81=D0=B2=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kvs_parser.py | 78 +++++++++++++--- kvs_pass1.py | 3 +- kvs_pass2.py | 117 ++++++++++++++++++++--- kvs_pass2_encoder_mutsize.py | 160 ++++++++++++++++++++++++++++++-- тесты/bss_bug.txt | 91 ++++++++++++++++++ тесты/bss_bug.квс | 53 +++++++++++ тесты/test_minimal2.квс | 34 +++++++ тесты/test_minimal3.квс | 50 ++++++++++ 8 files changed, 554 insertions(+), 32 deletions(-) create mode 100644 тесты/bss_bug.txt create mode 100644 тесты/bss_bug.квс create mode 100644 тесты/test_minimal2.квс create mode 100644 тесты/test_minimal3.квс diff --git a/kvs_parser.py b/kvs_parser.py index 190c15e..bf285e3 100644 --- a/kvs_parser.py +++ b/kvs_parser.py @@ -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 <вход.токены> <выход.аст>") diff --git a/kvs_pass1.py b/kvs_pass1.py index 7b1841c..72733b9 100644 --- a/kvs_pass1.py +++ b/kvs_pass1.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) diff --git a/kvs_pass2.py b/kvs_pass2.py index c5b832f..1fb8457 100644 --- a/kvs_pass2.py +++ b/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)}" diff --git a/kvs_pass2_encoder_mutsize.py b/kvs_pass2_encoder_mutsize.py index e815c43..0d3b873 100644 --- a/kvs_pass2_encoder_mutsize.py +++ b/kvs_pass2_encoder_mutsize.py @@ -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:рбикс']) """ # ----- Инструкции без операндов ----- diff --git a/тесты/bss_bug.txt b/тесты/bss_bug.txt new file mode 100644 index 0000000..8cb9439 --- /dev/null +++ b/тесты/bss_bug.txt @@ -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` не перезаписывается. \ No newline at end of file diff --git a/тесты/bss_bug.квс b/тесты/bss_bug.квс new file mode 100644 index 0000000..e581119 --- /dev/null +++ b/тесты/bss_bug.квс @@ -0,0 +1,53 @@ +.текст +.глобал _start + +_start: + ; ============================================= + ; ТЕСТ 1: Абсолютная адресация в .data + ; ============================================= + переместить_имм раикс, 0x42 + сохранить [абс_данные], раикс + + ; ============================================= + ; ТЕСТ 2: Косвенная адресация в .data + ; ============================================= + переместить_имм рбикс, косв_данные + переместить_имм раикс, 0x99 + сохранить [рбикс], раикс + + ; ============================================= + ; ТЕСТ 3: Косвенная адресация в .бнд + ; ============================================= + переместить_имм рсикс, неиниц_бнд + переместить_имм раикс, 0x77 + сохранить [рсикс], раикс + + ; ============================================= + ; Читаем всё обратно и проверяем + ; ============================================= + + ; Читаем абсолютную из .data + загрузить р8, [абс_данные] + + ; Читаем косвенную из .data + загрузить р9, [рбикс] + + ; Читаем косвенную из .бнд + загрузить р10, [рсикс] + + ; ============================================= + ; ЗАВЕРШЕНИЕ: возвращаем значение из абсолютной .data + ; ============================================= + загрузить рдиай, [абс_данные] + переместить_имм раикс, 60 + вызов_системы + +.данные +абс_данные: + .байт 0 +косв_данные: + .байт 0 + +.бнд +неиниц_бнд: + .резб 8 \ No newline at end of file diff --git a/тесты/test_minimal2.квс b/тесты/test_minimal2.квс new file mode 100644 index 0000000..5f9e0fd --- /dev/null +++ b/тесты/test_minimal2.квс @@ -0,0 +1,34 @@ +.текст +.глобал _start + +_start: + ; === ТЕСТ 1: Запись через косвенную адресацию === + ; Загружаем адрес переменной в регистр + переместить_имм рбикс, тестовая_переменная + + ; Записываем 42 через косвенную адресацию [рбикс] + переместить_имм раикс, 42 + сохранить [рбикс], раикс + + ; === ТЕСТ 2: Чтение через косвенную адресацию === + ; Загружаем адрес другой переменной в регистр + переместить_имм рсикс, вторая_переменная + + ; Читаем через косвенную адресацию [рсикс] + загрузить рдикс, [рсикс] + + ; === ТЕСТ 3: Проверяем, что записалось === + ; Читаем первую переменную через косвенную + загрузить рсикс, [рбикс] + + ; === ЗАВЕРШЕНИЕ === + ; Выходим с кодом из первой переменной (должно быть 42) + переместить_имм раикс, 60 + переместить рдиай, рсикс + вызов_системы + +.данные +тестовая_переменная: + .байт 0 +вторая_переменная: + .байт 99 \ No newline at end of file diff --git a/тесты/test_minimal3.квс b/тесты/test_minimal3.квс new file mode 100644 index 0000000..2f381d6 --- /dev/null +++ b/тесты/test_minimal3.квс @@ -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 байт под указатель на строку \ No newline at end of file