/pdp11-asm

pdp11-asm

Primary LanguageC++The UnlicenseUnlicense

pdp11-asm

Форк Ассемблер для архитектуры pdp-11, изначально разработанного Maksim Ovcharik. Данный форк отличается настройками выходного файла (предполагаемое смещение начала программы, размер программы), проверенной сборкой компилятором из набора GCC и изменением мнемоник команд для работы над словами (убран суффикс W для совпадения с официальной документацией PDP11/40).

Система команд

Метки

Задаются вначале строки, и могут содержать только _A-Z0-9, заканчиваеться метка должна :, и метка не может начинаться с цифры.

Регистры

Существует 8 регистров и 7 из них общего назначения. Обозначаются как R0 - R7, R7 - счетчик комманд, который изменяется после выполнения каждой команды автоматически.

Существует так же 8 режимов обращения к регистрам:

  1. Rn - непосредственное обращение к содержимому регистру

  2. @(Rn) - прямая адресация (по адресу в регистре)

  3. (Rn)+ - косвено-регистровая адресация 2-го уровня, с автоикрементом содержимого регистра

  4. @(Rn)+ - косвено-регистровая адресация 3-го уровня, с автоикрементом содержимого регистра

  5. -(Rn) - косвено-регистровая адресация 2-го уровня, с автодекрементом содержимого регистра

  6. @-(Rn) - косвено-регистровая адресация 3-го уровня, с автодекрементом содержимого регистра

  7. X(Rn) - индексная адресация, итоговый адрес получается как сумма индекса (X) и содержимого регистра

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

Программа должна содержать минимум одну секцию с кодом.

.CODE

Данная секция также должна содержать объявление глобальной функции (пока не используется, выполнение начинается с первой команды): .GLOBAL LABEL_NAME.

И в данной секции могут находиться только метки и команды.

.DATA

<label>: <type> <value>[, <fill>]

label - используется для задания имени переменной

type - допускаются к использованию следующие типы:

  • .STRING - значение (value) должно содержаться в скобках, при адресации, сразу после строки подставляется нулевой байт
  • .SPACE - используется совместно с fill, этот параметр указывает каким значение будет заполнены выделенная область
  • .BYTE, .WORD, .FLOAT - целые и вещественные данные, .FLOAT - 4 байта

.STACK

Нереализовано

Дополнительная информация

Регистр букв не имеет значения.

Комментарии начинаюся с # в начале строки.

Hello, World!

# 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

TO-DO

  • Константы