VAX-11/Система команд: различия между версиями
Panther (обсуждение | вклад) |
Panther (обсуждение | вклад) |
||
| Строка 329: | Строка 329: | ||
Режимы Cn–Fn полностью аналогичны байтовым режимам, за исключением размера операнда: word представляет два байта смещения в диапазоне −32768..+32767, longword — знаковые 4 байта. | Режимы Cn–Fn полностью аналогичны байтовым режимам, за исключением размера операнда: word представляет два байта смещения в диапазоне −32768..+32767, longword — знаковые 4 байта. | ||
При этом для PC-relative смещение считается от конца своего спецификатора вместе с байтами смещения — PC к тому моменту уже прошёл их все. | |||
=== PC-relative === | === PC-relative === | ||
Версия от 12:09, 10 апреля 2026
| Этот документ создан для Emuverse и распространяется на условиях лицензии CC-BY-SA-3.0. |
Регистры
| Номер (dec) | Номер (hex) | Имя | Назначение |
|---|---|---|---|
| 0 | 0 | R0 | Общего назначения; младший результат MUL/DIV |
| 1 | 1 | R1 | Общего назначения; старший результат MUL/DIV |
| 2 | 2 | R2 | Общего назначения |
| 3 | 3 | R3 | Общего назначения |
| 4 | 4 | R4 | Общего назначения |
| 5 | 5 | R5 | Общего назначения |
| 6 | 6 | R6 | Общего назначения |
| 7 | 7 | R7 | Общего назначения |
| 8 | 8 | R8 | Общего назначения |
| 9 | 9 | R9 | Общего назначения |
| 10 | A | R10 | Общего назначения |
| 11 | B | R11 | Общего назначения |
| 12 | C | R12 / AP | Argument Pointer — указатель на список аргументов процедуры |
| 13 | D | R13 / FP | Frame Pointer — указатель на фрейм стека |
| 14 | E | R14 / SP | Stack Pointer — указатель стека |
| 15 | F | R15 / PC | Program Counter — счётчик команд |
Все регистры (R0–R15/PC) 32-битные. Доступ к частям регистра производится через операнды с типами byte/word, читающие/пишущие младшие биты с sign/zero extension.
Кодирование инструкций VAX-11
Инструкции представляют собой поток байтов в следующем формате:
[ prefix byte ] [ opcode byte ] [ operand specifier ] [ operand specifier ] ...
- Prefix — только для двухбайтовых опкодов (FD, FE, FF для расширений)
- Opcode — 1 байт (большинство инструкций)
- Операнды следуют подряд, каждый описывается operand specifier-ом
Operand Specifier
Каждый операнд начинается с одного байта вида:
7 4 3 0 +-----+-----+ | mod | reg | +-----+-----+
| Байт (hex) | Режим | Синтаксис | Доп. байты | Примечание |
|---|---|---|---|---|
| 00–3F | Short Literal | #0..#63 | — | Биты 5–0 — значение; mod=00/01/02/03 |
| 4n | Index | [Rn] | — | Модификатор следующего спецификатора; масштаб по типу операнда |
| 5n | Register | Rn | — | Значение в регистре |
| 6n | Register Deferred | (Rn) | — | Содержимое Rn — адрес операнда |
| 7n | Autodecrement | -(Rn) | — | Rn уменьшается на размер типа до обращения |
| 8n | Autoincrement | (Rn)+ | — | Rn увеличивается на размер типа после обращения |
| 8F | Immediate | #imm | N байт | N = размер типа операнда; следует за спецификатором |
| 9n | Autoincrement Deferred | @(Rn)+ | — | Rn++ на 4 (longword-адрес) |
| 9F | Absolute | @#addr | 4 байта | Абсолютный 32-битный адрес |
| An | Byte Displacement | B^disp(Rn) | 1 байт | Знаковое смещение −128..+127 |
| AF | Byte Relative | B^label | 1 байт | Смещение от PC после спецификатора |
| Bn | Byte Displacement Deferred | @B^disp(Rn) | 1 байт | Адрес берётся из памяти по смещению |
| BF | Byte Relative Deferred | @B^label | 1 байт | |
| Cn | Word Displacement | W^disp(Rn) | 2 байта | Знаковое смещение −32768..+32767 |
| CF | Word Relative | W^label | 2 байта | |
| Dn | Word Displacement Deferred | @W^disp(Rn) | 2 байта | |
| DF | Word Relative Deferred | @W^label | 2 байта | |
| En | Longword Displacement | L^disp(Rn) | 4 байта | Полное 32-битное смещение |
| EF | Longword Relative | L^label | 4 байта | |
| Fn | Longword Displacement Deferred | @L^disp(Rn) | 4 байта | |
| FF | Longword Relative Deferred | @L^label | 4 байта |
Здесь n = номер регистра (0–E для R0–SP), F = PC (R15). Режимы с F в поле регистра — это PC-relative варианты соответствующих displacement-режимов, которые ассемблер выбирает автоматически по дистанции до метки.
0: Литеральный режим
Если биты 7–6 = 00, весь байт трактуется как 6-битный литерал (значения 0–63), независимо от поля reg. Это экономия на маленьких константах.
7 6 5 0 +---+---------------+ |0 0| value | → short literal, 0..63 +---+---------------+
4n: Индексный режим
mod=4 модифицирует следующий operand specifier. Эффективный адрес:
EA = base_EA + Rn * sizeof(тип операнда)
Масштаб берётся из типа инструкции, а не из спецификатора.
Пример: массив longword-ов
; int32_t table[10] — массив по абсолютному адресу ; R3 — индекс ; R0 — результат MOVL table[R3], R0
Кодирование:
MOVL opcode = D0 Операнд 1: table[R3] 43 — Index, Rn=3 EF — Longword Relative (base = PC + disp) xx xx xx xx — 4 байта смещения до table от PC Операнд 2: R0 50 — Register, R0 Итого: D0 43 EF <disp32> 50
Эффективный адрес = &table + R3 * 4 (MOVL → тип longword → масштаб 4)
5n — Регистровый
Операнд является значением в регистре.
Кодирование MOVL R2, R0:
D0 — MOVL 52 — Register, Rn=2 (источник) 50 — Register, Rn=0 (приёмник)
Ограничение: если инструкция требует операнд в памяти (например, PUSHAL, CALLS — адрес), Register не подходит — нужен адрес, а не значение.
6n — Регистровый косвенный
Регистр содержит адрес операнда. Одно обращение к памяти.
MOVL (R3), (R2) ; Mem[R2] := Mem[R3] Кодирование: D0 — MOVL 63 — Register Deferred, Rn=3 (источник) 62 — Register Deferred, Rn=2 (приёмник)
7n, 8n — Автодекрементный и автоинкрементный
- Автодекрементный: сначала регистр уменьшается на размер типа, затем используется как адрес.
- Автоинкрементный: Сначала используется текущее значение регистра как адрес, затем регистр увеличивается.
MOVL -(R2), R0 ; R2 -= 4; R0 := Mem[R2] MOVB -(R2), R0 ; R2 -= 1; R0 := Mem[R2] MOVW -(R2), R0 ; R2 -= 2; R0 := Mem[R2] MOVL (R2)+, R0 ; R0 := Mem[R2]; R2 += 4 MOVB (R2)+, R0 ; R0 := Mem[R2]; R2 += 1
Кодирование MOVL -(R2), R0:
D0 — MOVL 72 — Autodecrement, Rn=2 50 — Register R0
Пример: работа со стеком
; PUSH longword в R1: MOVL R1, -(SP) ; SP -= 4; Mem[SP] := R1 ; POP longword в R0: MOVL (SP)+, R0 ; R0 := Mem[SP]; SP += 4
Фактически, PUSHL / POPL обёртки над MOVL:
PUSHL R1 → MOVL R1, -(SP) ; нет POPL — используют MOVL (SP)+, Rn
Пример: обход массива
; R2 = указатель на начало массива int32_t
; R3 = счётчик элементов
loop:
MOVL (R2)+, R0 ; R0 := *p++
; ... обработка R0 ...
SOBGTR R3, loop ; R3--; если R3 > 0 — перейти
Каждую итерацию R2 автоматически продвигается на 4 — нет необходимости в ADDL.
Пример: копирование массива в цикле
MOVL (R2)+, (R3)+ ; Mem[R3] := Mem[R2]; R2+=4; R3+=4
Оба указателя продвигаются за одну инструкцию. Порядок: сначала вычисляются оба EA, затем оба регистра обновляются.
9n — Автоинкрементный косвенный
Происходит двойное разыменование: регистр содержит адрес адреса операнда. После чтения регистр увеличивается на 4 (всегда — longword, потому что читается адрес, а не данные).
EA = Mem[Rn] — читаем адрес из памяти Rn := Rn + 4 — продвигаем на размер указателя
Два обращения к памяти: сначала за адресом, потом за данными.
Сравнение с Autoincrement:
MOVL (R2)+, R0 ; R0 := Mem[R2]; R2 += 4 — читаем данные MOVL @(R2)+, R0 ; R0 := Mem[Mem[R2]]; R2 += 4 — читаем указатель, потом данные
Особый случай: @(PC)+ = Absolute 9F
Аналогично тому как (PC)+ = Immediate, @(PC)+ = Absolute:
EA = Mem[PC]; PC += 4
Следующие 4 байта в потоке команд — это сам 32-битный адрес операнда.
MOVL @#0x1234ABCD, R0 ; D0 9F CD AB 34 12 50 ; ^^— Autoincrement Deferred PC (= Absolute) ; ^^^^^^^^^^^— адрес операнда в потоке команд
An: С байтовым смещением
За байтом спецификатора следует 1 байт знакового смещения. Эффективный адрес:
EA = Rn + sign_extend(disp8)
Смещение знаковое: диапазон −128..+127 байт от базового регистра.
Кодирование
+------+------+ +----------+ | A | Rn | | disp8 | +------+------+ +----------+ байт 1 байт 2
MOVL 4(R2), R0 ; EA = R2 + 4 D0 — MOVL A2 — Byte Displacement, Rn=2 04 — смещение +4 50 — Register R0
MOVL -8(R2), R0 ; EA = R2 - 8 D0 — MOVL A2 — Byte Displacement, Rn=2 F8 — смещение −8 (0xF8 = −8 в дополнительном коде) 50 — Register R0
Главное применение — поля структур
// R2 = указатель на struct Point MOVL (R2), R0 ; R0 := p->x (offset 0 — Register Deferred, 1 байт) MOVL 4(R2), R1 ; R1 := p->y (offset 4 — Byte Displacement, 2 байта) MOVL 8(R2), R3 ; R3 := p->z (offset 8 — Byte Displacement, 2 байта)
Особый случай: Byte Relative — AF
Частный случай Byte Displacement с Rn=PC. Ассемблер использует его для переходов и обращений к статическим переменным в пределах ±127 байт от текущей инструкции:
MOVL near_var, R0 ; AF <disp8> — если near_var близко
В момент вычисления EA PC уже указывает на следующий байт после спецификатора, поэтому смещение считается от его конца.
Bn: С байтовым смещением косвенный
По вычисленному адресу читается указатель, и уже он используется как EA.
tmp = Rn + sign_extend(disp8) EA = Mem[tmp]
MOVL @4(R2), R0 ; tmp = R2+4; R0 := Mem[Mem[tmp]] D0 — MOVL B2 — Byte Displacement Deferred, Rn=2 04 — смещение +4 50 — Register R0
Cn–Fn: прочие режимы
Режимы Cn–Fn полностью аналогичны байтовым режимам, за исключением размера операнда: word представляет два байта смещения в диапазоне −32768..+32767, longword — знаковые 4 байта.
При этом для PC-relative смещение считается от конца своего спецификатора вместе с байтами смещения — PC к тому моменту уже прошёл их все.
PC-relative
PC-relative режимы (reg=F) это PC-relative addressing — assembler автоматически выбирает byte/word/longword displacement в зависимости от дистанции.
Пример: ADDL2 #5, R0 — прибавить 5 к регистру R0, размер операнда L, команда двухадресная
ADDL2 opcode = C0 #5 → immediate: mod=8, reg=F → байт 8F, затем 4 байта: 05 00 00 00 R0 → register: mod=5, reg=0 → байт 50 Итого: C0 8F 05 00 00 00 50