Еще один простой компилятор для PDP-11. Альтернативная история

Довольно давно я сделал версию компилятора языка TinyContext для миникомпьютера PDP-11. Это практически дословный перевод версии для MS-DOS и он может быть скомпилирован с ее помощью. Для тестирования использовался эмулятор simh и основной тест - компиляция собственного исходного кода был выполнен.

Но это совсем не равно созданию компилятора на реальной машине, тем более с нуля. Если есть машина без систем программирования и даже без операционной системы, то начать придется с ручной компиляции компилятора и ручной же пробивки перфолент или перфокарт с кодом компилятора и его исходным текстом. При этом неизбежно будут допущены ошибки и их надо будет как-то искать и исправлять. И второй вопрос - как сделать перфоленты, особенно перфоленту с кодом?

Со времени перевода компилятора на платформу PDP-11 прошло много времени, появились более близкие к реальности эмуляторы. Для всего последующего я использовал эмулятор PDP11/20 с отладчиком. К сожалению в нем нет перфоратора, поэтому временно вывод кода программы печатается на консоли.

PDP-11 - не первый в истории компьютер, до него уже много чего было, но допустим, что он был первым. Это машина с простым и удобным набором команд, написать для нее небольшую программу прямо в машинных кодах относительно просто, но написание большой программы - совсем непростая задача.

Я никогда не работал на PDP-11, разве что когда-то проходил мимо машины СМ-4, поэтому прежде чем что-то делать спросил у искуственного интеллекта, как задачу можно решить. Ответ был следующий:

Оценки затрат времени на кодирование и ввод следующие:

Делать это придется дваджы и затем сравнивать результаты. Для сравнения лент можно использовать машину.

Скорее всего нужна возможность разбивки и кода и исходного текста на несколько лент.

Я попробовал перевести в код самую первую функцию компилятора (mul), длина кода 83 слова, это заняло 100 минут. Сравнение со сгенерированным кодом заняло еще 11 минут. Если экстраполировать, то получится примерно 5689 минут на все - около 95 часов (100 * 4722 / 83). Но может быть, дальше будет быстрее?

Начал я с ошибки - при кодировании первой же строки M:=0; пропустил команду записи переменной M (010037/117226). При кодировании перехода в начало вложенного цикла я это заметил, сделал вставку двух команд, написанные на бумаге адреса решил не менять. Поскольку все переходы относительные, неверные адреса не должны были привести к ошибкам, но привели:

При сравнении со сгенерированным кодом я пропустил еще одну ошибку - неправильный код инструкции копирования содержимого регистра 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). Тоже не быстро.

Корректировка пробивкой всех отверстий на месте ошибки по-видимому нежелательна, поскольку увеличивает риск повреждения перфоленты. И как я понял, при пробивке символа, увидеть его на можно только после пробивки еще одного или двух символов, поэтому наиболее опасны ошибки в младшем разряде кода (или двух младших разрядах).

Стало ясно, что процесс компиляции и ввода должен быть изменен:

Перед кодированием функции необходимо было вычислить адреса глобальных переменных, на что ушло 19 минут, 11 минут собственно на вычисления и 8 минут на проверку. Но это разовые затраты, при переводе каждой следующей функции нужно вычислять адреса только ее переременных.

Перед этим я сделал еще две ошибки:

Обшие затраты на компиляцию и ввод примерно 110 часов. Для выявления ошибок сделать это придется дважды и не факт, что все заработает сразу.

Для компиляции в среде MS-DOS (теперь для этого нужен DOSBox/86Box или какой-нибудь другой эмулятор) нужно заменить все функции ввода-вывода от open() до halt() их аналогами, увеличить размер массива Text c 2048 символов до 16384 символов (ЭТО ВАЖНО!) и получить работающий в той же среде MS-DOS кросс-компилятор. С его помощью получить образ перфоленты с компилятором, работающим на PDP-11 без операционной системы.

Добавлена возможность смены ленты с исходным текстом и вывод текстовых представлений кодов.

Загрузчик кода стал сложнее, чтобы уместиться в 48 Кб он перемещен немного вниз:

137642: 012701 177550 MOV #177550,R1 137646: 012702 000400 MOV #400,R2 137652: 005003 CLR R3 137654: 005211 INC @R1 137656: 032711 100200 BIT #100200,@R1 137662: 100430 BMI 137744 137664: 001774 BEQ 137656 137666: 116100 000002 MOVB 2(R1),R0 137672: 122700 000067 CMPB #67,R0 137676: 103421 BCS 137742 137700: 122700 000015 CMPB #15,R0 137704: 001763 BEQ 137654 137706: 122700 000057 CMPB #57,R0 137712: 103007 BCC 137732 137714: 042700 177770 BIC #177770,R0 137720: 006303 ASL R3 137722: 006303 ASL R3 137724: 006303 ASL R3 137726: 060003 ADD R0,R3 137730: 000751 BR 137654 137732: 001002 BNE 137740 137734: 010302 MOV R3,R2 137736: 000745 BR 137652 137740: 010322 MOV R3,(R2)+ 137742: 000743 BR 137652 137744: 000000 HALT

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

e 137642 012701 177550 012702 400 005003 005211 032711 100200 100430 001774 116100 000002 122700 67 103421 122700 15 1763 122700 57 103007 042700 177770 006303 006303 006303 060003 000751 001002 010302 000745 010322 000743 000000

Здесь 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<nCode do punch(mod(Code[pCode],256)); punch(div(Code[pCode],256)); pCode:=pCode+1; end end word close() is return 0; end char putc(char C) is char C1:=C; inline 0x15C1, 0xFF74; // 012701 177564 // mov #serial, R1 inline 0x9031, 0x0002; // 110061 000002 // movb R0, 2(R1) inline 0x8BC9; // 105711 // tstb (R1) inline 0x80FE; // 100376 // bpl end char wait() is putc('?'); inline 0x15C1, 0xFF70; // 012701 177560 // mov #RCSR, R1 inline 0x0A89; // 005211 // inc (R1) inline 0x8BC9; // 105711 // tstb (R1) inline 0x80FE; // 100376 // bpl inline 0x9C40, 0x0002; // 116100 000002 // movb 2(R1), R0 //inline 0x0A9F, 0xFF70; // 005237 177560 // inc (#RCSR) //inline 0x8BDF, 0xFF70; // 105737 177560 // tstb (#RCSR) //inline 0x80FD; // 100375 // bpl //inline 0x97C0, 0xFF72; // 113700 177562 // movb (177562), R0 putc(char(13)); putc(char(10)); end word halt() is inline 0x0000; end word Init() is Heap[ 0]:='0'; Heap[ 1]:='1'; Heap[ 2]:='2'; Heap[ 3]:='3'; Heap[ 4]:='4'; Heap[ 5]:='5'; Heap[ 6]:='6'; Heap[ 7]:='7'; Heap[ 8]:='8'; Heap[ 9]:='9'; Heap[10]:='A'; Heap[11]:='B'; Heap[12]:='C'; Heap[13]:='D'; Heap[14]:='E'; Heap[15]:='F'; Heap[16]:='c'; Heap[17]:='h'; Heap[18]:='a'; Heap[19]:='r'; Heap[20]:= char(0); Heap[21]:='b'; Heap[22]:='y'; Heap[23]:='t'; Heap[24]:='e'; Heap[25]:= char(0); Heap[26]:='w'; Heap[27]:='o'; Heap[28]:='r'; Heap[29]:='d'; Heap[30]:= char(0); Heap[31]:='b'; Heap[32]:='e'; Heap[33]:='g'; Heap[34]:='i'; Heap[35]:='n'; Heap[36]:= char(0); Heap[37]:='i'; Heap[38]:='f'; Heap[39]:= char(0); Heap[40]:='w'; Heap[41]:='h'; Heap[42]:='i'; Heap[43]:='l'; Heap[44]:='e'; Heap[45]:= char(0); Heap[46]:='i'; Heap[47]:='n'; Heap[48]:='l'; Heap[49]:='i'; Heap[50]:='n'; Heap[51]:='e'; Heap[52]:= char(0); Heap[53]:='r'; Heap[54]:='e'; Heap[55]:='t'; Heap[56]:='u'; Heap[57]:='r'; Heap[58]:='n'; Heap[59]:= char(0); Heap[60]:='e'; Heap[61]:='n'; Heap[62]:='d'; Heap[63]:= char(0); Heap[64]:='c'; Heap[65]:='.'; Heap[66]:='p'; Heap[67]:='r'; Heap[68]:='g'; Heap[69]:= char(0); Heap[70]:='c'; Heap[71]:='.'; Heap[72]:='c'; Heap[73]:='o'; Heap[74]:='m'; Heap[75]:= char(0); nHeap :=76; Name[ 0]:=16; Cls [ 0]:= 1; Size[ 0]:= 1; Name[ 1]:=21; Cls [ 1]:= 1; Size[ 1]:= 1; Name[ 2]:=26; Cls [ 2]:= 1; Size[ 2]:= 2; nName := 3; pStk := 0; nCode := 0; nData := 16640; end word Push(word V) is Stk[pStk]:=V; pStk:=pStk+1; end word Pop () is pStk:=pStk-1; return Stk[pStk]; end word putn(word N, word B) is //pStk:=0; word I:=1; while I!=0 do Push (mod(N,B)); N :=div(N,B); I :=N; end while pStk!=0 do putc(char(Pop()+48)); end end word Stop() is putc (char(10)); putn (nLine,10); close(); halt (); end word val () is word E:=10; word I:= 0; if Buff[0]='0' then if Buff[1]='c' then E:= 8; I:= 2; end if Buff[1]='x' then E:=16; I:= 2; end end word N:=0; while Buff[I]!=char(0) do word K:=0; while Heap[K]!=Buff[I] do if K=E then Stop(); end K:=K+1; end N:=mul(E,N); N:=N+K; I:=I+1; end return N; end char Line [ 128]; word pChar; word nChar; word ReadLine() is char DEL:=char(127); word NC :=128; pChar:=0; nChar:=0; word Flag:=0; while Flag =0 do if pText>=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 char Ch:=Text[pText]; pText := pText+1; if Ch!=DEL then Line[nChar]:= Ch; nChar :=nChar+1; if Ch=char(10) then Flag:=1; end end if Ch =DEL then nChar :=0; 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)<nName then Stop(); end word pBuff:=0; char Ch :=char(1); while Ch !=char(0) do Ch := Buff[pBuff]; Heap[nHeap]:= Ch; nHeap :=nHeap+1; pBuff :=pBuff+1; end Scan(); return nName; end char Var (word Subclass) is Cls [nName]:= 2; Sub [nName]:= Subclass; Size[nName]:= 1; Ofs [nName]:=nData; if Buff[0]='[' then if Subclass!=0 then Stop(); end Scan(); Size[nName]:=val(); Scan(); // ] Scan(); // ; end nData :=nData+(mul(Size[Type[nName]],Size[nName])); if mod(nData,2)!=0 then nData :=nData+1; end nName :=nName+1; return Buff[0]; end word Hide() is word I:=Pop(); while I< nName do Heap[Name[I]]:=char(0); I :=I+1; end end word ReserveInt() is while mod(nCode+nCode,16)!=0 do Emi2(0); end end word Reserve() is ReserveInt(); Emi2(0); ReserveInt(); end word Patch(word pCode) is Code[pCode+1]:=(nCode+nCode)-((pCode+pCode)+4); end //word rFlag; word Ctrl() is word cFlag:=0; //if cFlag =0 then if Comp(37)=0 then // if Cond(); //Push(nCode); //Emi2(0x0077); // 000167 // jmp ? //nCode:=nCode+1; Push(nName); Scan(); Ctrl(); while Comp(60)!=0 do // !end Ctrl(); end Hide(); Patch(Pop()); //rFlag:=1; // 14.05.2006 cFlag:=1; end //end if cFlag =0 then if Comp(40)=0 then // while Push(nCode); Cond(); //Push(nCode); //Emi2(0x0077); // 000167 // jmp ? //nCode:=nCode+1; Push(nName); Scan(); Ctrl(); while Comp(60)!=0 do // !end Ctrl(); end Hide(); word pExit:=Pop(); word pLoop:=Pop(); Emi2 (0x0077); // 000167 // jmp Ofs Emi2 ((0xFFFF-((nCode+nCode+2)-(pLoop+pLoop)))+1); Patch(pExit); //rFlag:=1; // 14.05.2006 cFlag:=1; end end if cFlag =0 then if Comp(46)=0 then // inline Buff[0]:=','; while Buff[0]=',' do Scan(); Emi2(val()); // dw Val Scan(); end //rFlag:=1; // 14.05.2006 cFlag:=1; end end if cFlag =0 then if Comp(53)=0 then // return Scan(); Expr(); Emi2(0x0087); // 000207 // rts //rFlag:=0; // 14.05.2006 cFlag:=1; end end if cFlag =0 then word I:=Find(0); if Cls[I]=1 then word N:=Obj(I); if Var(2)=':' then Scan(); Expr(); Assign(N); end end if Cls[I]=2 then if Size[I]>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

функции ввода-вывода для кросс-компилятора:

word open() is inline 0xB4, 0x3D; // mov AH, 3DH inline 0xB0, 0x00; // mov AL, 00H inline 0xBA, 0x4A, 0xC1; // mov DX, @@Data+Ofs(Heap[64]) inline 0xCD, 0x21; // int 21H end word create() is inline 0xB4, 0x3C; // mov AH, 03CH inline 0xB9, 0x00, 0x00; // mov CX, 00H inline 0xBA, 0x50, 0xC1; // mov DX, @@Data+Ofs(Heap[70]) inline 0xCD, 0x21; // int 21H end word read() is inline 0xB4, 0x3F; // mov AH, 3FH inline 0x8B, 0x1E, 0x08, 0xC1; // mov BX, word [@@DATA+Ofs(hFile)] inline 0xB9, 0x00, 0x40; // mov CX, 16384 inline 0xBA, 0x00, 0x41; // mov DX, @@DATA+Ofs(Text) inline 0xCD, 0x21; // int 21H end word write() is inline 0xB4, 0x40; // mov AH, 40H inline 0x8B, 0x1E, 0x08, 0xC1; // mov BX, word [@@DATA+Ofs(hFile)] inline 0x8B, 0x0E, 0x06, 0xC1; // mov CX, word [@@DATA+Ofs(nCode)] inline 0x03, 0xC9; // add CX, CX inline 0xBA, 0x06, 0x81; // mov DX, @@DATA+Ofs(Code) inline 0xCD, 0x21; // int 21H end word close() is inline 0xB4, 0x3E; // mov AH, 3EH inline 0x8B, 0x1E, 0x08, 0xC1; // mov BX, word [@@DATA+Ofs(hFile)] inline 0xCD, 0x21; // int 21H end char putc(char C) is char C1:=C; inline 0x92; // xchg DX, AX inline 0xB4, 0x02; // mov AH, 2 inline 0xCD, 0x21; // int 21H end char wait() is inline 0x90; end word halt() is inline 0xB8, 0x00, 0x4C; // mov AX, 4C00H inline 0xCD, 0x21; // int 21H end

Top.Mail.Ru

Сайт создан в системе uCoz