косвенная адресация

This commit is contained in:
mzhoot 2026-05-17 15:18:35 +03:00
parent ea01681c81
commit 9ba53693d5
8 changed files with 554 additions and 32 deletions

View file

@ -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 <вход.токены> <выход.аст>")

View file

@ -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)

View file

@ -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)}"

View file

@ -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
View 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
View 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

View file

@ -0,0 +1,34 @@
.текст
.глобал _start
_start:
; === ТЕСТ 1: Запись через косвенную адресацию ===
; Загружаем адрес переменной в регистр
переместить_имм рбикс, тестовая_переменная
; Записываем 42 через косвенную адресацию [рбикс]
переместить_имм раикс, 42
сохранить [рбикс], раикс
; === ТЕСТ 2: Чтение через косвенную адресацию ===
; Загружаем адрес другой переменной в регистр
переместить_имм рсикс, вторая_переменная
; Читаем через косвенную адресацию [рсикс]
загрузить рдикс, [рсикс]
; === ТЕСТ 3: Проверяем, что записалось ===
; Читаем первую переменную через косвенную
загрузить рсикс, [рбикс]
; === ЗАВЕРШЕНИЕ ===
; Выходим с кодом из первой переменной (должно быть 42)
переместить_имм раикс, 60
переместить рдиай, рсикс
вызов_системы
.данные
тестовая_переменная:
.байт 0
вторая_переменная:
.байт 99

View 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 байт под указатель на строку