Форк Ассемблер для архитектуры pdp-11, изначально разработанного Maksim Ovcharik. Данный форк отличается настройками выходного файла (предполагаемое смещение начала программы, размер программы), проверенной сборкой компилятором из набора GCC и изменением мнемоник команд для работы над словами (убран суффикс W для совпадения с официальной документацией PDP11/40).
Задаются вначале строки, и могут содержать только _A-Z0-9
, заканчиваеться метка должна :
, и метка не может начинаться с цифры.
Существует 8 регистров и 7 из них общего назначения. Обозначаются как R0
- R7
, R7
- счетчик комманд, который изменяется после выполнения каждой команды автоматически.
Существует так же 8 режимов обращения к регистрам:
-
Rn
- непосредственное обращение к содержимому регистру -
@(Rn)
- прямая адресация (по адресу в регистре) -
(Rn)+
- косвено-регистровая адресация 2-го уровня, с автоикрементом содержимого регистра -
@(Rn)+
- косвено-регистровая адресация 3-го уровня, с автоикрементом содержимого регистра -
-(Rn)
- косвено-регистровая адресация 2-го уровня, с автодекрементом содержимого регистра -
@-(Rn)
- косвено-регистровая адресация 3-го уровня, с автодекрементом содержимого регистра -
X(Rn)
- индексная адресация, итоговый адрес получается как сумма индекса (X
) и содержимого регистра -
@X(Rn)
- косвенно-индексная адресация, адрес получается как сумма индекса (X
) и содержимого регистра
Регистры в командах описываются так же, как представленно выше.
Непосредственно значение можно просто записать на месте операнда, при записи значения допускается использование различных систем счисления
0\d+
- восьмеричная
(-)\d+
- десятичная
0x\d+
- шеснадцатиричная
При записи значения в скобках, оно будет восприниматься как адрес (используется 3 режим для регистра R7
)
Также на месте операнда можно указать метку, при сборке, вместо метки подставится ее адрес.
Пока есть возможность использования только адресов меток.
В некоторых командах содержатся модификаторы размера: B
- операция над байтом.
<command> <operand>
CLR
, CLRB
- обнуление операнда
INC
, INCB
- инкрементация
DEC
, DECB
- декриментация
COM
, COMB
- инверсия
TST
, TSTB
- проверка
NEG
, NEGB
- отрицание
ASR
, ASRB
- арифметический сдвиг вправо
ASL
, ASLB
- арифметический сдвиг влево
ROR
, RORB
- циклический сдвиг вправо
ROL
, ROLB
- циклический сдвиг влево
ADC
, ADCB
- добавления флага переноса C
к операнду
SBC
, SBCW
- вычитание флага переноса C
к операнду
SWAB
- перестановка байтов
SXT
- знаковое преобразование из 16-разрядного числа в 32-х
<command> <source>, <destination>
MOV
, MOVB
- пересылка значения
CMP
, CMPB
- сравнение
BIT
, BITB
- проверка разрядов
BIC
, BICB
- отчистка разрядов
BIS
, BISB
- установка разрядов
ADD
- сложение, результат заносится в приемник
SUB
- вычитание, значение приемника минус значение источника, результат заносится в приемник
<command> <registr source>, <destination>
В командах данного типа, в качестве источника может выступать только значение регистра, на другие значение компилятор выдаст ошибку, на этапе компиляции.
MUL
- умножение, результат заносится в два регистра, в Rn
.R(n+1)
, Rn
- регистр указанный в качестве приемника. Если n
нечетное, запоминается только младшие 16 разрядов резултата
DIV
- деление, Rn
.R(n+1)
/ (dest)
, в Rn
- заносится частное, в R(n+1)
- остаток. n
должно быть четным.
ASH
- сдвиг содержимного регистра приемника, на заданное количество разрядов. Учитываются при сдвиге только младшие шесть разрядов и рассматриваются они как число в дополнительном коде, в зависимости от знака числа выбирается направление сдвига.
ASHC
- аналогично ASH
только сдвигается двойное слово: Rn
.R(n+1)
XOR
- исключающее или
<command> <register>
Регистр в данном случае должен ссылаться на память, где лежат операнды:
X
- (register)
Y
- (register + 4)
Вещественное число представляется двумя словами, в формате числа с одинарной точностью (http://en.wikipedia.org/wiki/Single_precision_floating-point_format).
Порядок операндов: X := X (op) Y
Команды: FADD
, FSUB
, FMUL
, FDIV
<command> <diff>
Адрес перехода вычисляется следующим образом: addr = R7 + (diff * 2)
. diff
- имеет разрядность одного байта. Таким образом можно осуществить максимальный относительный переход на 200 слов-команд.
При использовании меток, нужная разница высчитывается автоматически при компиляции, если полученный адрес превышает допустимое значение выдается ошибка.
BR
- безуслоный
BNE
- по ненулю
BEQ
- по нулю
BPL
- по плюсу
BMI
- по минусу
BVC
- по отсутствию переполнения
BVS
- по переполнению
BCC
- по отсутствию переноса
BCS
- по переносу
BGE
- по "не меньше нуля"
BLT
- по "меньше нуля"
BGT
- по "больше нуля"
BLE
- по "не больше нуля"
BHI
- беззнаковый по "больше нуля"
BLOS
- беззнаковый по "не больше нуля"
JMP
- абсолютный безусловный переход, адресом для перехода является значение операнда. JMP <operand>
.
JSR
- переход к подпрограмме. JSR <reg>, <operand>
. Сначала увеличивается содержимое регистра стека (R6
) на два, после чего значение регистра reg
сохраняется в стекке (по адресу R6-2
), далее в регистр reg
заносится текущее значение R7
, и в R7
заносится значение операнда
RST
- возврат из подпрограммы. RST <reg>
. R7
присваивается значение регистра reg
, а регистру reg
значение из стека (R6)
.
MARK
- возврат из подпрограммы с отчисткой стека. MARK <N>
. Значение регистра стека увеличивается на 2 * N
, после чего осуществляется переход по адресу из стека. (?) В методичке по которой написан компилятор криво описана эта команда, и в эмуляторе она не реализована, так что я не уверен что все так происходит.
SOB
- переход по счетчику. SOB <counter reg> <diff>
. diff
состоит из 6 разрядов, и в нем содержется беззнаковое число, означающее количество слов, на которое будет совершен переход назад. counter reg
регситр счетчик, при выполнения данной команды, содержимое регистра декриментируется, и если оно не равно нулю осуществляется переход на адрес для перехода. Адрес для перехода, высчитывается как addr = R7 - (diff * 2)
. При использовании меток адрес высчитывается автоматически, если полученное значение выходит за рамки 6 разрядов, выдается ошибка на этапе компиляции.
EMP <byte>
, TRAP <byte>
, IOT
, BPT
- команды прерываний*
Следующие команды не используют дополнительных параметров
RTI
, RTT
- возврат из прерывания*
RESET
- сброс*
WAIT
- ожидание прерывания*
*
- реализованы, но из-за недостаточной документации не знаю что они делают
HALT
- остановка выполнения
CLC
, CLV
, CLZ
, CLN
- команды сброса соответсвующий флагов
SEC
, SEV
, SEZ
, SEN
- команды установки соответсвующий флагов
CLALL
, SEALL
- сброс, установка всех флагов
NOP
- просто nop
.SECTION (.CODE|.DATA|.STACK)
Программа должна содержать минимум одну секцию с кодом.
Данная секция также должна содержать объявление глобальной функции (пока не используется, выполнение начинается с первой команды): .GLOBAL LABEL_NAME
.
И в данной секции могут находиться только метки и команды.
<label>: <type> <value>[, <fill>]
label
- используется для задания имени переменной
type
- допускаются к использованию следующие типы:
.STRING
- значение (value
) должно содержаться в скобках, при адресации, сразу после строки подставляется нулевой байт.SPACE
- используется совместно сfill
, этот параметр указывает каким значение будет заполнены выделенная область.BYTE
,.WORD
,.FLOAT
- целые и вещественные данные,.FLOAT
- 4 байта
Нереализовано
Регистр букв не имеет значения.
Комментарии начинаюся с #
в начале строки.
# acd address: 0177566
# acd check address: 0177564
.SECTION .DATA
HELLO_STR: .STRING "HELLO, WORLD!"
.SECTION .CODE
.GLOBAL MAIN
MAIN:
MOV HELLO_STR, R0
MOV 13, R1
LOOP:
TSTB @(R0)
BEQ END_LOOP
MOVB (R0)+, (0177566)
ADC_LOOP:
BIT 0200, (0177564)
BEQ ADC_LOOP
SOB R1, LOOP
END_LOOP:
HALT
- Константы