Язык Форт и его реализации

       

Реализация встроенного ассемблера


Приводимый ниже текст является законченной реализацией встроенного структурного ассемблера для форт-систем на базе микропроцессора К580. В оттранслированном виде он занимает около 1300 байт и является типичным для 8-разрядных микропроцессоров. Для 16- или 32-разрядных процессоров в силу их большей сложности объем текста больше. Но даже в этом случае объем исходного текста и скомпилированного кода существенно меньше, чем для традиционных ассемблеров.

( FORTH-83 АССЕМБЛЕР ДЛЯ К580 ТЕМЯШКИНО 1985 ) VOCABULARY ASSEMBLER ASSEMBLER DEFINITIONS ( МАШИННЫЕ КОМАНДЫ ) DECIMAL : 8* 2* 2* 2* ; 4 CONSTANT H 5 CONSTANT L 7 CONSTANT A 6 CONSTANT PSW 2 CONSTANT D 3 CONSTANT E 0 CONSTANT B 1 CONSTANT C 6 CONSTANT M 6 CONSTANT SP : 1MI ( ---> ) CREATE C, DOES> C@ C, ; : 2MI ( ---> ) CREATE C, DOES> C@ + C, ; : 3MI ( ---> ) CREATE C, DOES> C@ SWAP 8* + C, ; : 4MI ( ---> ) CREATE C, DOES> C@ C, C, ; : 5MI ( ---> ) CREATE C, DOES> C@ C, , ;

HEX 00 1MI NOP 76 1MI HLT F3 1MI DI FB 1MI EI 07 1MI RLC 0F 1MI RRC 17 1MI RAL 1F 1MI RAR E9 1MI PCHL F9 1MI SPHL E3 1MI XTHL EB 1MI XCHG 27 1MI DAA 2F 1MI CMA 37 1MI STC 3F 1MI CMC 80 2MI ADD 88 2MI ADC 90 2MI SUB 98 2MI SBB A0 2MI ANA A8 2MI XRA B0 2MI ORA B8 2MI CMP 09 3MI DAD C1 3MI POP C5 3MI PUSH 02 3MI STAX 0A 3MI LDAX 04 3MI INR 05 3MI DCR 03 3MI INX 0B 3MI DCX C7 3MI RST D3 4MI OUT DB 4MI IN C6 4MI ADI CE 4MI ACI D6 4MI SUI DE 4MI SBI E6 4MI ANI EE 4MI XRI F6 4MI ORI FE 4MI CPI 22 5MI SHLD 2A 5MI LHLD 32 5MI STA 3A 5MI LDA C4 5MI CNZ CC 5MI CZ D4 5MI CNC DC 5MI CC E4 5MI CPO EC 5MI CPE F4 5MI CP FC 5MI CM CD 5MI CALL C9 1MI RET C3 5MI JMP C0 1MI RNZ C8 1MI RZ D0 1MI RNC D8 1MI RC E0 1MI RPO E8 1MI RPE F0 1MI RP F8 1MI RM C2 5MI JNZ CA 5MI JZ D2 5MI JNC DA 5MI JC E2 5MI JPO EA 5MI JPE F2 5MI JP FA 5MI JM : MOV SWAP 8* 40 + + C, ; : MVI SWAP 8* 6 + C, C, ; : LXI SWAP 8* 1 + C, , ; ( КОДЫ УСЛОВИЙ ДЛЯ СТРУКТУР УПРАВЛЕНИЯ ) C2 CONSTANT 0= D2 CONSTANT CS E2 CONSTANT PE F2 CONSTANT 0< ( СТРУКТУРЫ УПРАВЛЕНИЯ ) DECIMAL : NOT ( N:КОД УСЛОВИЯ ---> N1) 8 + ; : THEN ( АДР,2 ---> ) 2 ?PAIRS HERE SWAP ! ; : IF ( КОД ---> АДР,2 ) C, HERE 0 , 2 ; : ELSE ( АДР,2 ---> АДР1,2) 2 ?PAIRS C3 IF ROT SWAP THEN 2 ; : BEGIN ( ---> АДР,1 ) HERE 1 ; : UNTIL ( АДР,1,КОД --->) SWAP 1 ?PAIRS С, , ; : AGAIN ( АДР,1 --->) 1 ?PAIRS C3 C, , ; : WHILE ( АДР,1,КОД ---> АДР,1,АДР1,4) IF 2 + ; : REPEAT ( АДР,1,АДР1,4 --->) >R >R AGAIN R> R> 2- THEN ;


( РАБОТА С МЕТКАМИ ) 10 CONSTANT LBLMAX ( МАКСИМАЛЬНОЕ ЧИСЛО ЛОКАЛЬНЫХ МЕТОК) VARIABLE LTABLE LBLMAX 1+ 2* ALLOT 10 CONSTANT FRMAX ( МАКСИМАЛЬНОЕ ЧИСЛО ССЫЛОК ВПЕРЕД) VARIABLE FRTABLE FRMAX 2* 2* ALLOT : FRCHK ( ---> ПРОВЕРКА НЕРАЗРЕШЕННЫХ ССЫЛОК ВПЕРЕД ) FRMAX 0 DO I 2* 2* FRTABLE + @ ABORT" НЕРАЗРЕШЕННАЯ ССЫЛКА ВПЕРЕД" LOOP ; : FRCLR ( ---> ИНИЦИАЛИЗАЦИЯ ТАБЛИЦЫ ССЫЛОК ВПЕРЕД ) FRTABLE FRMAX 2* 2* ERASE LTABLE LBLMAX 1+ 2* ERASE ;

: FRRES ( N:МЕТКА ---> РАЗРЕШЕНИЕ ССЫЛОК ВПЕРЕД ) FRMAX 0 DO I 2* 2* FRTABLE + 2DUP @ = IF HERE OVER 2+ @ +! 0! ELSE DROP THEN LOOP ; : FRADD ( N:МЕТКА ---> ДОБАВЛЕНИЕ ССЫЛКИ ВПЕРЕД В ТАБЛИЦУ ) FRMAX 1+ 0 DO FRMAX I = ABORT" СЛИШКОМ МНОГО ССЫЛОК ВПЕРЕД" I 2* 2* FRTABLE + DUP @ 0= IF 2DUP ! HERE 1+ SWAP 2+ ! LEAVE ELSE DROP THEN LOOP ;

( ОПРЕДЕЛЯЮЩИЕ ВХОЖДЕНИЯ МЕТОК ) : !LT CREATE , DOES> @ FRRES HERE SWAP 2* LTABLE + ! ; ( ИСПОЛЬЗУЮЩИЕ ВХОЖДЕНИЯ МЕТОК ) : @LT CREATE , DOES> @ DUP 2* LTABLE + @ SWAP OVER 0= IF FRADD THEN DROP ; 1 !LT 1#: 2 !LT 2#: 3 !LT 3#: 4 !LT 4#: 5 !LT 5#: 6 !LT 6#: 7 !LT 7#: 8 !LT 8#: 9 !LT 9#: 10 !LT 10#: 1 @LT 1# 2 @LT 2# 3 @LT 3# 4 @LT 4# 5 @LT 5# 6 @LT 6# 7 @LT 7# 8 @LT 8# 9 @LT 9# 10 @LT 10# ( ПЕРЕКЛКЮЧЕНИЕ В АССЕМБЛЕР ) FORTH DEFINITIONS : BEG-ASM [ ASSEMBLER ] FRCLR [ FORTH ] ASSEMBLER !CSP ; : END-ASM [ ASSEMBLER ] FRCHK [ FORTH ] ?CSP;

В приведенном тексте можно выделить четыре группы определений: машинные команды, структуры управления, метки и стандартные слова для создания определений в машинном коде. Помимо них данный текст определяет ряд вспомогательных слов.

Машинные команды на языке ассемблера записываются в обратной польской форме, принятой для языка Форт: <операнды> <операция>, где <операнды> — слова, вычисляющие на стеке размещения операндов данной машинной команды, а <операция> — ее мнемоника. Для обозначения регистров микропроцессора К580 зарезервированы слова А В С D E H L, для задания регистровых пар используются слова В D H SP PSW, для косвенной адресации через регистровую пару HL — слово М.


При исполнении слов, обозначающих регистры, регистровые пары и косвенную адресацию, на стеке оставляются значения, соответствующие принятым в системе команд К580 обозначениям для регистров и регистровых пар:

A B C D E H L M SP PSW 7 0 1 2 3 4 5 6 6 6

При исполнении мнемоники машинной команды на вершину словаря компилируется соответствующая машинная инструкция. Размещения операндов при этом снимаются со стека. Порядок вычисления операндов машинной команды соответствуют порядку написания операндов на обычном языке ассемблера.

Ниже приведены для сравнения записи машинных команд на обычном языке ассемблера и на языке ассемблера данной форт-системы:

HLT HLT DAD SP SP DAD PUSH PSW PSW PUSH CMP C C CMP MOV A,M A M MOV MVI B,2 B 2 MVI LXI D,120+8 D 120 8 + LXI CALL 5002H HEX 5002 CALL DECIMAL



Для определения мнемоник используются определяющие слова, соответствующие форматам машинных команд (1MI, ..., 5MI). Все они имеют одинаковую создающую часть, которая компилирует в поле параметров статьи для мнемоники однобайтный код маски для кода данной команды, который снимается со стека. Исполняющая часть определений, используя маску и размещения операндов, которые она снимает со стека, компилирует двоичный код, соответствующий данной команде. Так, например, через слово 1MI определяются машинные команды, не имеющие операндов. При исполнении мнемоники такой команды на вершину словаря компилируется однобайтный код операции. Слово 3MI определяет мнемоники команд, имеющих один операнд — номер регистра. Этот номер занимает разряды со 2 по 4 в однобайтном коде команды. Поэтому исполняющая часть сдвигает свой операнд — номер регистра — влево на 3 разряда исполнением слова 8* и добавляет к нему маску команды. Получившийся однобайтный код компилируется на вершину словаря. Три команды — MOV, MVI и LXI — не подходят под описанные общие форматы, поэтому они определяются непосредственно через двоеточие.

Структурные операторы встроенного ассемблера позволяют программировать разветвления и циклы без явно заданных команд перехода на метку.


Их синтаксис аналогичен синтаксису соответствующих операторов языка Форт.

Условный оператор в полной или сокращенной форме записывается следующим образом:

<КОД-УСЛОВИЯ> IF <ЧАСТЬ-ТО> ELSE <ЧАСТЬ-ИНАЧЕ> THEN <КОД-УСЛОВИЯ> IF <ЧАСТЬ-ТО> THEN

Часть «то» выполняется, только если в разрядах PSW установлен заданный «код условия», в противном случае выполняется часть «иначе», если она есть.

Циклы могут быть записаны в одной из трех форм:

BEGIN <ТЕЛО-ЦИКЛА> AGAIN BEGIN <ТЕЛО-ЦИКЛА> <КОД-УСЛОВИЯ> UNTIL BEGIN <ТЕЛО-1> <КОД-УСЛОВИЯ> WHILE <ТЕЛО-2> REPEAT

Цикл BEGIN-AGAIN является бесконечным, BEGIN-UNTIL прекращает выполнение при указанном коде условия, в цикле BEGIN-WHILE-REPEAT, наоборот, указанный код условия задает продолжение цикла.

Для указания кода условия в ассемблере используются следующие слова: 0= — установлен флаг Z, 0< — флаг S, CS — флаг C, PE — флаг P. Для инвертирования кода условия используется слово NOT.

Структурные операторы компилируют на своем месте команды безусловных и условных переходов с указанным условием.

Для работы с явно задаваемыми метками в ассемблер включены слова вида 1#: 2#: ... 10#: и 1# 2# ... 10#. Слова первой группы задают определение метки, слова второй — использование. Метки имеют строго локальный характер, т.е. эти слова можно использовать только внутри определений через CODE и между словами ;CODE и END-CODE. Наряду с метками допускается использование ассемблерных структур управления.

Локальные метки рекомендуется использовать только в трехбайтных командах типа JMP, CALL, LXI. Уже определенные локальные метки могут участвовать в арифметических и стековых операциях; использующие вхождения меток, определяющие вхождения которых вводятся позднее (ссылки вперед), могут участвовать только в операциях, сводящихся к добавлению к ним числа со знаком.

Для реализации работы с метками ассемблер имеет таблицу меток LTABLE и таблицу ссылок вперед FRTABLE.


Их размер определяет максимальное число разных меток и ссылок вперед в пределах одного определения в машинном коде (от слова CODE или ;CODE до слова END-CODE). Для каждой возможной метки соответствующий элемент таблицы LTABLE содержит нуль, если метка еще не определена, или адрес, который является ее значением, если метка уже определена. Входом в эту таблицу служит номер метки. В таблице FRTABLE для каждой ссылки вперед хранятся два значения: номер метки и адрес в словаре, по которому нужно вписать ее значение. По этому адресу скомпилировано значение смещения, которое нужно добавить как число со знаком к значению данной метки.

Слова для использующих вхождений меток определяются через @LT, например 1 @LT 1#. Исполнение определенного таким образом слова 1#, обозначающего метку номер 1, состоит в обращении к таблице LTABLE по индексу 1. Если там стоит нуль, т.е. метка номер 1 еще не определена, то этот нуль выдается на стек в качестве смещения от значения метки, которое определится позднее. Одновременно в таблицу FRTABLE заносится запись о данной ссылке вперед. В качестве адреса, куда нужно будет впоследствии вписать значение метки, берется HERE 1+. Здесь используется тот факт, что в микропроцессоре К580 операнд-адрес занимает 2 байта, следующие за однобайтным кодом операции.

Слова для определения меток определяются через слово !LT, например 1 !LT 1#:. Исполнение определенного таким образом слова 1#: определяет метку номер 1 как текущий адрес вершины словаря HERE. Его действие состоит в том, что выполняется просмотр таблицы FRTABLE с разрешением всех накопившихся ссылок на данную метку, после чего запись о данной метке заносится в таблицу LTABLE.

Последнюю группу определений составляют слова для доступа к встроенному ассемблеру при компиляции машинного кода. Помимо стандартных форт-слов CODE, ;CODE и END-CODE здесь определяются слова LABEL и NEXT;. Первое используется для создания именованных подпрограмм, к которым можно обращаться из машинного кода. Второе является сокращением для часто встречающегося окончания ассемблерных определений: NEXT JMP END-CODE.


В качестве примера приведем определение слова ><, которое меняет местами байты в переданном на стеке двухбайтном значении:

CODE >< ( N1 ---> N2) H POP A L MOV L H MOV H A MOV H PUSH NEXT;

Приведенная реализация встроенного ассемблера может быть усовершенствована по нескольким направлениям. Прежде всего, увеличив константы LBLMAX и FRMAX и добавив новые определения для слов, обозначающих метки, можно увеличить количество разных меток и ссылок вперед, которые разрешается использовать в ассемблерном определении. Далее можно ввести контроль правильности операндов, включив соответствующие проверки в исполняющую часть определяющих слов для мнемоник команд. Для проверки числа элементов на стеке обычно используется глобальная ячейка CSP и слово !CSP, которое засылает в нее текущий адрес вершины стека. Интересным расширением является возможность введения макрокоманд. Макрокоманды без локальных меток можно определять обычным образом через двоеточие:

INRN ( R:РЕГИСТР,N:ЧИСЛО РАЗ ---> ) [ ABSEMBLER ] 0 DO DUP INR LOOP DROP ;

При исполнении текста А 3 INRN будут скомпилированы три команды А INR.

Если же макрорасширение должно порождать обращения к адресам через локальные метки, то потребуется более основательное расширение ассемблера.


Содержание раздела