VAX-11/Система команд: различия между версиями

Материал из Emuverse
Строка 325: Строка 325:
50  — Register R0
50  — Register R0
</pre>
</pre>
=== Cn&ndash;Fn: прочие режимы ===
Режимы Cn&ndash;Fn полностью аналогичны байтовым режимам, за исключением размера операнда: word представляет два байта смещения в диапазоне −32768..+32767, longword &mdash; знаковые 4 байта.


=== PC-relative ===
=== PC-relative ===

Версия от 12:02, 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 близко

PC уже указывает на следующую инструкцию в момент вычисления EA, поэтому смещение считается от конца спецификатора.

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