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

Материал из Emuverse
Строка 232: Строка 232:


Оба указателя продвигаются за одну инструкцию. Порядок: сначала вычисляются оба EA, затем оба регистра обновляются.
Оба указателя продвигаются за одну инструкцию. Порядок: сначала вычисляются оба EA, затем оба регистра обновляются.
=== 9n — Автоинкрементный косвенный ===
Происходит двойное разыменование: регистр содержит адрес адреса операнда. После чтения регистр увеличивается на 4 (всегда — longword, потому что читается адрес, а не данные).
<pre>
EA  = Mem[Rn]    — читаем адрес из памяти
Rn := Rn + 4      — продвигаем на размер указателя
</pre>
Два обращения к памяти: сначала за адресом, потом за данными.
Сравнение с Autoincrement:
<pre>
MOVL    (R2)+,  R0  ; R0 := Mem[R2];        R2 += 4  — читаем данные
MOVL    @(R2)+, R0  ; R0 := Mem[Mem[R2]];  R2 += 4  — читаем указатель, потом данные
</pre>
'''Особый случай: <code>@(PC)+</code> = Absolute 9F'''
Аналогично тому как <code>(PC)+</code> = Immediate, <code>@(PC)+</code> = Absolute:
EA = Mem[PC];  PC += 4
Следующие 4 байта в потоке команд — это сам 32-битный адрес операнда.
<pre>
MOVL    @#0x1234ABCD, R0
; D0  9F  CD AB 34 12  50
;    ^^— Autoincrement Deferred PC (= Absolute)
;        ^^^^^^^^^^^— адрес операнда в потоке команд
</pre>


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

Версия от 11:45, 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)
;        ^^^^^^^^^^^— адрес операнда в потоке команд

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