Еще один простой компилятор для PDP-11. Альтернативная история
Довольно давно я сделал версию компилятора языка TinyContext
для миникомпьютера PDP-11. Это практически дословный перевод версии для MS-DOS
и он может быть скомпилирован с ее помощью. Для тестирования использовался эмулятор
simh и основной тест - компиляция собственного
исходного кода был выполнен.
Но это совсем не равно созданию компилятора на реальной машине, тем более с нуля. Если есть
машина без систем программирования и даже без операционной системы, то начать придется с ручной
компиляции компилятора и ручной же пробивки перфолент или перфокарт с кодом компилятора
и его исходным текстом. При этом неизбежно будут допущены ошибки и их надо будет как-то искать
и исправлять. И второй вопрос - как сделать перфоленты, особенно перфоленту с кодом?
Со времени перевода компилятора на платформу PDP-11 прошло много времени, появились более близкие
к реальности эмуляторы. Для всего последующего я использовал эмулятор
PDP11/20 с отладчиком.
К сожалению в нем нет перфоратора, поэтому временно вывод кода программы печатается на консоли.
PDP-11 - не первый в истории компьютер, до него уже много чего было, но допустим, что он был первым.
Это машина с простым и удобным набором команд, написать для нее небольшую программу прямо в машинных
кодах относительно просто, но написание большой программы - совсем непростая задача.
Я никогда не работал на PDP-11, разве что когда-то проходил мимо машины СМ-4, поэтому прежде чем
что-то делать спросил у искуственного интеллекта, как задачу можно решить. Ответ был следующий:
Машина имеет панель управления с переключателями и линейками индикаторов, показывающих
текущий адрес и слово данных. Ее можно использовать для ввода программы и начинать необходимо
с этого, но такой способ ввода сложный и медленный.
Также есть телепринтер (телеnайп?) Model 33 ASR, который помимо своих основных функций ввода
и вывода текста позволяет пробить перфоленту, причем подключение к машине для этого не требуется.
Пробить можно не любые символы, так что ввод произвольных кодов невозможен. Это ограничение
можно обойти если вместо кодов вводить их текстовые представления. Длина ленты увеличится почти
в шесть раз, но ее хотя бы можно сделать и ее легче читать. Программа загрузки должна преобразовать
последовательности восьмеричных цифр в коды. Телепринтер также может использоваться для ввода
перфоленты в машину, но работает он медленно - за секунду он может прочитать всего 10 символов.
На загрузку кода компилятора потребуется примерно 40 минут.
Есть более быстродействующее устройство чтения/пробивки перфолент PC-05, за секунду оно может прочитать
300 символов в непрерывном режиме и 20 символов при чтении по одному символу. На эмуляторе simh этого
не видно - чтение происходит очень быстро.
Оценки затрат времени на кодирование и ввод следующие:
Перевод одной ассемблерной команды в код - 30-60 секунд, примерно 50 часов на весь код.
Ввод - 4-6 часов или неделю(!) если использовать переключатели на передней панели.
Делать это придется дваджы и затем сравнивать результаты. Для сравнения лент можно использовать машину.
Скорее всего нужна возможность разбивки и кода и исходного текста на несколько лент.
Я попробовал перевести в код самую первую функцию компилятора (mul), длина кода 83 слова,
это заняло 100 минут. Сравнение со сгенерированным кодом заняло еще 11 минут. Если экстраполировать,
то получится примерно 5689 минут на все - около 95 часов (100 * 4722 / 83). Но может быть, дальше
будет быстрее?
Начал я с ошибки - при кодировании первой же строки M:=0; пропустил команду записи
переменной M (010037/117226). При кодировании перехода в начало вложенного цикла
я это заметил, сделал вставку двух команд, написанные на бумаге адреса решил не менять. Поскольку
все переходы относительные, неверные адреса не должны были привести к ошибкам, но привели:
При кодировании перехода в начало внешнего цикла я зачем-то пересчитал адреса
и получил 000640 вместо 000642. В десятичной системе
это было бы правильно (636 + 4 = 642), но здесь то система восьмеричная.
В результате длина перехода 224 вместо 226 и, соответственно,
неверое смещение 177554 = 2000000 - 224 вместо 177552 = 200000 - 226.
Неправильно дополнил код нулями. В исходном варианте их не было, здесь же я решил в конце
каждой функции резервировать минимум восемь слов для возможных исправлений.
При сравнении со сгенерированным кодом я пропустил еще одну ошибку - неправильный код инструкции копирования
содержимого регистра R0 в R1. На бумаге был написан код 0100001 вместо 10001.
Обнаружилось после ввода инструкций в файл и сравнения файлов с помощью diff.
К слову, код функции значительно больше, чем он мог бы быть. При ручном кодировании можно уложиться в 22 слова.
Длина кода функции init() 710 слов, ее я не переводил целиком, но чтобы оценить минимально необходимый размер
достаточно перевести в код три строки. Получится 271 слово.
Примитивный распределитель регистров позволяет уменьшить длину кода init() до 541 слова, но смысла в нем нет.
Он сокращает код компилятора меньше чем на 5 процентов, а возможность сделать ошибку наверняка увеличивает.
Самый первый вариант компилятора содержал одну незначительную оптимизацию (исключение выдачи повторных команд
возврата из функции) и она была сделана неправильно.
В принципе можно выполнить более-менее оптимальную компиляцию вручную, это сократит затраты на ввод кода,
но не факт, что сократит затраты на компиляцию.
Пробивка ленты занимает меньше времени, на первую попытку ввода 85 инструкций (83 слова функции mul
и 2 слова перехода к главной функции) я потратил 7 минут и понял, что запутался. Вторая попытка тоже
оказалась неудачной. Четыре ошибки были замечены сразу при вводе и исправлены (BACKSPACE+DELETE/RUBOUT),
один код введен неверно из-за ошибки при компиляции (не при вводе!). И все это было впустую, поскольку
в начале я забыл пробить стартовый адрес.
Третья попытка выполнялась на модифицированном эмуляторе телепринтера
github.com/progs-n-things/asr33emu.
Модификация состояла в замене кодов символов для клавиш ] (закрывающая квадратная скобка), \ (обратный
слеш) и ' (кавычка) на <LF> (перевод строки), <CR> (возврат каретки) и <DELETE/RUBOUT>
(удаление) соответственно. Это сделало клавиатуру ПК похожей на клавиатуру телепринтера и дало
естественный способ ввода символа <LF> (при нажатии клавиши Enter вводится <CR>,
чтобы ввести символ <LF> нужно нажать комбинацию клавиш Ctrl-J, но это путь к лишним ошибкам.
При отключенной печати на бумаге (т.е. только при пробивке перфоленты) на ввод тех же 85 инструкций
я потратил 17 минут. Наверное, отверстия на перфоленте читаются хуже, чем символы на бумаге (и наверное,
на реальном устройстве они читаются еще хуже). Я сделал пять ошибок, которые заметил и исправил. В итоге
одна ошибка - в самом конце кода ввел 1777<DELETE/RUBOUT>17755213700 (нет <LF> между 177552
и 13700). Но такой перфолента все-таки можно пользоваться. Надо лишь сделать еще одну ленту с исправлениями.
По-видимому я не учел, что пробитый на перфоленте код становится видимым только после пробивки еще
нескольких кодов и печать на бумаге все-таки нужна для контроля ввода. Чтобы не тратить зря бумагу
можно печатать несколько кодов в одной строке и разделять их пробелами. При приближении к правому краю
нужно завершить ввод кода, затем нажать клавишу <CR>, затем <LF>. Порядок нажатия этих клавиш
в принципе не важен, но так сложилось, по крайней мере на ПК с системой MS-DOS. При загрузке ленты
код <CR> следует игнорировать.
Четвертая попытка (с включенной печатью на бумаге) заняла 15 минут. Были допущены и сразу исправлены три
ошибки, в остальном лента корректна.
Общие затраты на пробивку лент примерно 833 минут - около 14 часов (15 * 4722 / 85). Тоже не быстро.
Корректировка пробивкой всех отверстий на месте ошибки по-видимому нежелательна, поскольку увеличивает риск
повреждения перфоленты. И как я понял, при пробивке символа, увидеть его на можно только после пробивки
еще одного или двух символов, поэтому наиболее опасны ошибки в младшем разряде кода (или двух младших разрядах).
Стало ясно, что процесс компиляции и ввода должен быть изменен:
При компиляции функции на бумаге надо писать стартовый адрес.
Нужен способ указать адрес в любом месте ленты, а не только в начале, например, можно принять
соглашение, что символ '/' (прямой слеш) означает, что перед ним был введен адрес, любой другой
символ с кодом меньшим, чем у '/' означает, что был введен код (но предпочтительны символы
пробел и <LF>). Это даст возможность исправить только что сделанную ошибку.
Прямой слеш удобен по двум причинам. В таблице ASCII он непосредственно предшествует нулю,
что немного упрощает загрузчик, и он удобно расположен на клавиатуре телепринтера - вторая
справа клавиша в нижнем ряду, слева от клавиши смены регистра - почти как на клавиатуре ПК.
Для указания, что введенный код ошибочен можно использовать любой символ с кодом большим, чем у цифры 7,
но предпочтительно использовать <DELETE/RUBOUT>, поскольку его легче всего прочитать
(все отверстия пробиты).
Возможно, имеет смысл пробивать на перфоленте адреса, кратные 108 или 208
(или только младшие цифры адресов). Завершать их можно также символом <DELETE/RUBOUT>.
Перед кодированием функции необходимо было вычислить адреса глобальных переменных, на что
ушло 19 минут, 11 минут собственно на вычисления и 8 минут на проверку. Но это разовые затраты,
при переводе каждой следующей функции нужно вычислять адреса только ее переременных.
Перед этим я сделал еще две ошибки:
Вычислил адреса в десятичной системе (видиимо, думая потом их преобразовать в восьмеричную),
при этом не учел, что переменная Ofs - это массив и неправильно назначил адреса
всем переменным начиная с nName. Времени потратил 14 минут.
При повторении вычисления уже в восьмеричной системе неправильно вычислил адрес
переменной nCode. Вместо 104406 получил 050406,
ошибка произошла из-за неправильного перевода длины массива Code в восьмеричную
систему и я прибавил 4000 вместо 40000. Адреса всех следующих переменных
тоже получились неправильными. Времени потратил 23 минуты, но часть его ушла на то чтобы научиться
считать в восьмеричной системе. Если бы константы в исходном тексте были записаны в восьмеричной
системе, было бы проще. Кодов команд это тоже касается, но комментарии с восьмеричными кодами всех
команд все были написаны.
Обшие затраты на компиляцию и ввод примерно 110 часов. Для выявления ошибок сделать это придется
дважды и не факт, что все заработает сразу.
Для компиляции в среде MS-DOS (теперь для этого нужен DOSBox/86Box или какой-нибудь другой эмулятор)
нужно заменить все функции ввода-вывода от open() до halt() их аналогами,
увеличить размер массива Text c 2048 символов до 16384 символов (ЭТО ВАЖНО!) и получить работающий
в той же среде MS-DOS кросс-компилятор. С его помощью получить образ перфоленты с компилятором,
работающим на PDP-11 без операционной системы.
Добавлена возможность смены ленты с исходным текстом и вывод текстовых представлений кодов.
Загрузчик кода стал сложнее, чтобы уместиться в 48 Кб он перемещен немного вниз:
Можно ввести этот код с помощью переключателей на панели управления и могут быть ошибки.
После завешения ввода нужно проверить введенное и ошибки исправить. Или можно в отладчике
выполнить команду:
Здесь 137642 - стартовый адрес загрузчика. Посмотреть ассемблерный листинг можно в отладчике введя команду
u 137642 l32
Я пробовал вести код загрузчика с промощью переключателей, на это ушло 13 минут. Еще 7 минут были потрачены
на проверку - ошибок не было. В принципе ничего сложного, ввод начинается с набора стартового адреса
и установки его нажатием клавиши <Load>. Затем последовательно набираются коды команд. Ввод каждой
завершается нажатием клавиши <Deposit>. При этом адрес автоматически увеличивается на 2.
Важно!Сразу после нажатия клавиши <Deposit> на индикаторах отображается адрес и код введенной
команды. Здесь необходимо остановиться и проверить корректность ввода. Нажатие клавиши <Exam> недопустимо!
И еще одно замечание. При наборе всех команд кроме первой на индикаторах отображается адрес и код предыдущей
команды. При наборе первой команды на индикаторах отображается ее адрес, код может быть любым.
Если при вводе допущена ошибка, то нужно установить адрес ошибочной команды и затем ввести ее код. Если команда
пропущена, то придется ввести ее и все последующие.
Экстраполяция затрат на ввод позволяет оценить время, необходимое для ввода с помощью переключателей кода
компилятора. Это примерно 1805 минут, больше 30 часов (13 * 4722 / 34). При использовании телепринтера
затраты около 14 часов, в 2-3 раза больше, чем было обещано искусственным интеллектом. Возможно, я медленно
печатаю, но в любом случае печатать на клавиатуре намного легче, чем устанавливать переключатели.
И кроме кода нужно ввести еще и исходный текст. По идее это проще, но как исправлять ошибки?
Первая мысль - разбивать текст на небольшие части и пробивать каждую на отдельной перфоленте.
При обнаружении ошибки пробивать заново всю ленту с ошибкой. Ну или вырезать кусок с ошибкой
и пробваить маленькую ленту с исправлением - одна лента превратится в три. И наверное, нужна
программа, которая читает несколькько и пробивает все коды с них на одно перфоленте.
Можно сделать и нечто похожее на пропуск ошибочного кода - при пробивке перед нажатием
клавиш <CR> и <LF> проверять введенную строку и, при обнаружении ошибки,
нажимать <DELETE/RUBOUT>. Затем вводить строку повторно. При компиляции придется
копировать одну строку текста в небольшой массив и, если встретится символ <DELETE/RUBOUT>,
то игнорировать все предшествующие символы. Естественно, от ошибок в самой программе это не спасает,
но тут ничего не сделаешь.
Перфорарты позволили бы исправлять ошибки проще, но по сценарию есть только перфоратор
и считыватель перфоленты - устройства более простые и дешевые, чем перфоратор и считыватель
перфокарт. Первый компилятор языка Фортран разрабатывался на машине IBM 704, в состав которой
входили считыватель и перфоратор перфокарт (модели 711 и 721 соответственно).
Оценка скорости пробивки исходного текста следующая. На пробивку текста той же функции mul
я потратил 5 минут. При этом допустил одну ошибку, которую сразу исправил, в одном месте
не пробил необязательный пробел, в другом пробил лишний. Длина функции mul 16 строк/216 байт,
длина компилятора примерно 1090 строк/19812 байт (числа могут немного меняться из-за
модификаций текста). Если экстраполировать на набор всего текста, то получится 341-459 минут
(5 * 1090 / 16 - 5 * 19812 / 216), 6-8 часов. Набор текста той же функции в обычном редакторе
занял примерно 2 минуты. Правда, была допущена ошибка - вместо присваивания T:=A
напечатал T:=0. Ошибка приводит к бесконечному циклу в функции резервирования места
для возможных исправлений кода. Ошибки неизбжны, именно эта ошибка была бы замечена при сравнении
нового кода компилятора со старым и/или при зацикливании нового кода при его исполнении.
В реальности первая версия Tiny Context для MS-DOS была написана за три дня, это заведомо не больше
24 часов чистого времени (скорее всего меньше). В это время также входило написание части кода
на бумаге, поэтому вряд ли на набор текста было потрачено 6-8 часов. Да, это другое - использовался
более-менее удобный редактор и проверка текста выполнялась имеющимся компилятором. Но даже если
на ввод текста нужно 8 часов - это далеко не самая большая часть затрат.
Исходный текст компилятора с необходимыми изменениями, пока не окончательный:
char Text [ 2048];
word pText;
word nText;
word nLine;
word Code [ 8192];
word nCode;
word hFile;
char Heap [ 2048];
word nHeap;
word Name [ 256];
word Cls [ 256];
word Sub [ 256];
word Type [ 256];
word Size [ 256];
word Ofs [ 256];
word nName;
word nData;
word Stk [ 128];
word pStk;
char Buff [ 128];
word mul (word A, word B) is
word M:=0;
while B >0 do
word T :=A;
word I :=1;
while B-I>=I do
T:=T+T;
I:=I+I;
end
M:=M+T;
B:=B-I;
end
return M;
end
word div (word A, word B) is
word D:=0;
while A>=B do
word T :=B;
word I :=1;
while A-T>=T do
T:=T+T;
I:=I+I;
end
A:=A-T;
D:=D+I;
end
return D;
end
word mod (word A, word B) is
return A-mul(div(A,B),B);
end
word open() is
return 0;
end
word create() is
return 0;
end
word getc() is
inline 0x15C1, 0xFF68; // 012701 177550 // mov #TRS, R1
inline 0x0A89; // 005211 // inc (R1)
inline 0x35C9, 0x8080; // 032711 100200 // bit #100200, (R1)
inline 0x8104; // 100404 // bmi
inline 0x03FC; // 001774 // beq
inline 0x9C40, 0x0002; // 116100 000002 // movb 2(R1), R0
inline 0x0102; // 000402 // br
inline 0x15C0, 0xFF00; // 012700 177400 // mov #ERR, R0
end
word read() is
word P:=0;
word W:=getc();
while W!=0xFF00 do
Text[P]:=char(W);
P :=P+1;
if P>=2048 then
return P;
end
W:=getc();
end
return P;
end
char punch(word B) is
word B1:=B;
inline 0x15C1, 0xFF6C; // 012701 177554 // mov #TPS, R1
inline 0x9031, 0x0002; // 110061 000002 // movb R0, 2(R1)
inline 0x15C0, 0x0000; // 012700 000000 // mov #0, R0
inline 0x35C9, 0x8080; // 032711 100200 // bit #100200, (R1)
inline 0x8102; // 100402 // bmi
inline 0x03FC; // 001774 // beq
inline 0x0102; // 000402 // br
inline 0x15C0, 0x0001; // 012700 000001 // mov #1, R0
end
word write() is
word pCode:=0;
while pCode=nText then
pText :=0;
nText :=read();
if nText=0 then
wait();
nText :=read();
end
//if pText>=nText then
if nText=0 then
//return char(0);
Stop();
end
end
if nChar=NC then
Flag:=1;
end
end
end
char Look() is
if pChar>=nChar then
ReadLine();
end
//return Text[pText];
return Line[pChar];
end
char Read() is
char Ch:=Look();
if Ch =char(10) then
nLine :=nLine+1;
end
//pText :=pText+1;
pChar :=pChar+1;
putc (Ch);
return Ch;
end
word isalnum() is
if 'A'<=Look() then
if Look()<='Z' then
return 0;
end
end
if 'a'<=Look() then
if Look()<='z' then
return 0;
end
end
if '0'<=Look() then
if Look()<='9' then
return 0;
end
end
return 1;
end
word Digraph(char C1, char C2) is
if Buff[0]=C1 then
if Look()=C2 then
Buff[1]:=Read();
Buff[2]:=char(0);
end
end
end
char Scan() is
word pBuff:=0;
while pBuff =0 do
word sFlag:=0;
while sFlag =0 do
if Look()!=char( 0) then
if Look()!=char( 9) then
if Look()!=char(10) then
if Look()!=char(13) then
if Look()!=char(32) then
sFlag:=1;
end
end
end
end
end
if sFlag=0 then
Read();
end
end
while isalnum()=0 do
Buff[pBuff]:= Read();
pBuff :=pBuff+1;
end
if pBuff=0 then
Buff[pBuff]:= Read();
pBuff :=pBuff+1;
end
Buff[pBuff] :=char(0);
Digraph('<', '=');
Digraph('!', '=');
Digraph('>', '=');
Digraph(':', '=');
if Buff[0]='/' then
if Look()='/' then
while Look()!=char(10) do
if Read()=char(0) then
Stop();
end
end
pBuff:=0;
end
end
end
end
word Comp(word pHeap) is
word pBuff:=0;
while Buff[pBuff]=Heap[pHeap] do
if Buff[pBuff]=char(0) then
return 0;
end
pHeap:=pHeap+1;
pBuff:=pBuff+1;
end
return 1;
end
word Find(word fFlag) is
word pName:=0;
while pName< nName do
if Comp(Name[pName])=0 then
return pName;
end
pName:=pName+1;
end
if fFlag=0 then
Stop();
end
return pName;
end
word Emi2(word W) is
Code[nCode]:=W;
nCode:=nCode+1;
end
word Assign(word I) is
if Size[I]>1 then
Emi2(0x1581); // 012601 // mov (SP)+, R1
if Size[Type[I]]=1 then
Emi2(0x9031); // 110061 // movb R0, Adr(R1)
end
if Size[Type[I]]=2 then
Emi2(0x1031); // 010061 // mov R0, Adr(R1)
end
end
if Size[I]=1 then
if Size[Type[I]]=1 then
Emi2(0x901F); // 110037 // movb R0, @#Adr
end
if Size[Type[I]]=2 then
Emi2(0x101F); // 010037 // mov R0, @#Adr
end
end
Emi2(Ofs[I]);
end
word Expr() is
word eFlag:=0;
//if eFlag =0 then
if '0'<=Buff[0] then
if Buff[0]<='9' then
Emi2(0x15C0); // 012700 // mov #Val, R0
Emi2(val());
eFlag:=1;
end
end
if Buff[0]=''' then
//TODO 128..255
Emi2(0x15C0); // 012700 // mov #Val, R0
Emi2(word(Read()));
Read();
eFlag:=1;
end
if Buff[0]='(' then
Scan();
Expr();
eFlag:=1;
end
//end
if eFlag =0 then
word I:=Find(0);
if Cls[I]=1 then
Push(I);
Scan(); // (
Scan();
Expr();
I:=Pop();
end
if Cls[I]=2 then
if Size[I]>1 then
Push(I);
Scan(); // [
Scan();
Expr();
I:=Pop();
if Size[Type[I]]=1 then
//TODO Use CLR/BISB
Emi2(0x9C00); // 116000 // movb Adr(R0), R0
end
if Size[Type[I]]=2 then
Emi2(0x0CC0); // 006300 // asl R0
Emi2(0x1C00); // 016000 // mov Adr(R0), R0
end
end
if Size[I]=1 then
if Size[Type[I]]=1 then
//TODO Use CLR/BISB
Emi2(0x97C0); // 113700 // movb @#Adr, R0
end
if Size[Type[I]]=2 then
Emi2(0x17C0); // 013700 // mov @#Adr, R0
end
end
Emi2(Ofs[I]);
//INFO Added 22.03.2026
if Size[Type[I]]=1 then
Emi2(0x45C0); // 042700 // bic #177400, R0
Emi2(0xFF00);
end
end
if Cls[I]=3 then
Scan(); // (
Push(I);
Sub[nName]:= 0;
word J:=I+1;
while Sub[J]=1 do
Push(J);
Scan();
Expr();
J:=Pop();
Assign(J);
J:=J+1;
end
I:=Pop();
if J=I+1 then
Scan(); // )
end
word W:=(0xFFFF-(((nCode+nCode)+4)-Ofs[I]))+1;
Emi2(0x09F7); // 004767 // jsr Ofs
Emi2(W);
end
end
Scan();
word I:=0;
if Buff[0]='+' then
I:=1;
end
if Buff[0]='-' then
I:=2;
end
if I!=0 then
Emi2(0x1026); // 010046 // mov R0, -(SP)
Push(I);
Scan();
Expr();
I:=Pop();
if I=1 then
Emi2(0x1581); // 012601 // mov (SP)+, R1
Emi2(0x6040); // 060100 // add R0, R1
end
if I=2 then
Emi2(0x1001); // 010001 // mov R0, R1
Emi2(0x1580); // 012600 // mov (SP)+, R0
Emi2(0xE040); // 160100 // sub R0, R1
end
end
end
word Cond() is
Scan();
Expr();
//TODO
word jCode:=0;
if Buff[0]='<' then
jCode:=0x8700; // 1034XX // blo Ofs
if Buff[1]='=' then
jCode:=0x8300; // 1014XX // blos Ofs
end
end
if Buff[0]='=' then
jCode:=0x0300; // 0014XX // beq Ofs
end
if Buff[0]='!' then
jCode:=0x0200; // 0010XX // bne Ofs
end
if Buff[0]='>' then
jCode:=0x8200; // 1010XX // bhi Ofs
if Buff[1]='=' then
jCode:=0x8600; // 1030XX // bhis Ofs
end
end
if jCode=0 then
Stop();
end
Emi2(0x1026); // 010046 // mov R0, -(SP)
Scan();
Expr();
Emi2(0x1581); // 012601 // mov (SP)+, R1
//Emi2(0xE001); // 160001 // sub R1, R0
Emi2(0xE001); // 160001 // sub R0, R1 //R1 - R0
Emi2(jCode+2); // bxx 2
Push(nCode);
Emi2(0x0077); // 000167 // jmp ?
//nCode:=nCode+1;
Emi2(0);
end
word Obj (word T) is
if Cls[T]!=1 then
Stop();
end
Name[nName]:=nHeap;
Type[nName]:= T;
Scan();
if Find(1)1 then
Scan(); // [
Scan();
Expr();
if Size[Type[I]]=2 then
Emi2(0x0CC0); // 006300 // asl R0
end
Emi2(0x1026); // 010046 // mov R0, -(SP)
end
Scan(); // :=
Scan();
Expr();
Assign(I);
end
if Cls[I]=3 then
Expr();
end
//rFlag:=1; // 14.05.2006
end
Scan();
end
word Func(word Rts) is
Scan();
Ctrl();
while Comp(60)!=0 do // !end
Ctrl();
end
//if rFlag!=0 then // 14.05.2006 // if rFlag=0 then
//Emi2(0x0087); // 000207 // rts
Emi2(Rts);
//end
end
word dump() is
putc(char(10));
putn(256,8);
putc('/');
word pCode:=0;
while pCode< nCode do
putn(Code[pCode],8);
putc(char(10));
pCode:=pCode+1;
end
end
begin
Init();
hFile:=open();
pChar:=0;
nChar:=0;
pText:=0;
nText:=0;
nLine:=1;
Emi2(0x0077); // 000167 // jmp ?
//nCode:=nCode+1;
Emi2(0);
Scan();
while Comp(31)!=0 do // !begin
Obj (Find(0));
char Ch:=Buff[0];
if Ch ='(' then
Cls [nName]:= 3;
Sub [nName]:= 0;
Ofs [nName]:=nCode+nCode;
nName :=nName+1;
Push(nName);
Scan();
if Buff[0]!=')' then
Obj (Find(0));
while Var(1)=',' do
Scan();
Obj (Find(0));
end
end
Scan(); // is
Func(0x0087);
Reserve();
Hide();
end
if Ch!='(' then
Var(0);
end
Scan();
end
Patch(0);
Emi2 (0x15C6); // 012706 137640 // mov #012706, SP
Emi2 (0xBFA0);
Func (0x0000);
close();
//hFile:=create();
//write();
//close();
dump ();
end