Продолжаю разбирать эпические работы c «International Obfuscated C Code Contest», в этот раз рассказ пойдет о победителе 2018 года.

Как думаете, насколько эпичную работу надо создать, чтобы получить приз «Best of show» на конкурсе для самых отбитых и отмороженных крутых программистов на свете?
Кто
ты воинта «фантастическая тварь», которая такое смогла?
Я тоже задался этими интересными вопросами, как только прочел описание работы, так и появилась на свет эта статья.
Эмулятор
Фантастический автор проекта Christopher Mills участвует в конкурсе IOCCC практически с самого основания — c 1990го года и его работы неоднократно выбирались лучшими в разных номинациях. Так что данная работа — отнюдь не внезапное откровение а результат многолетнего труда, сложных изысканий и большой практики.
Начну с оригинального описания:
The program delivered here is both a full machine emulation of the original PDP-7 that Ken Thompson used to write the first version of UNIX and a full machine emulation of the PDP-11/40 used by subsequent UNIXes.
Да, это программный эмулятор двух машин PDP-7 и PDP-11/40 из ооочень далеких времен, позволяющий запускать три интересные (в историческом плане) операционные системы.
Чтобы вы имели представление о компьютерах тех лет, DEC PDP-7 выглядел примерно так:

Согласно описанию автора, эмулируется следующее железо:
PDP-7 Central Processor.
Core Memory Module Type 147 – extends the core to 8,192 18-bit words.
Extended Arithmetic Element Type 177 – adds one’s-complement multiplication, division and shifting.
Real Time Clock.
Teletype Model 33 KSR.
Perforated Tape Reader Type 444.
RB09 Fixed Disk Controller.
Для большего антуража, показываю как выглядел Teletype Model 33:

А вот так выглядел DEC PDP-11/40:

С ним у автора все еще веселее:
So if I have a PDP-7 emulator, how do I run operating systems that expect a PDP-11? Simple… I emulate a PDP-11/40 on the PDP-7.
Пожалуйста даже не спрашивайте как такое реализовано, поскольку это эта дичь уже совсем за гранью моих знаний о компьютерах.
Эмулируется следующее железо:
PDP-11/40 (KD11-A).
EIS instruction set (KE11-E).
Memory management unit (KJ11-A).
Line time clock (KT11-D).
124 Kwords of memory (244 Kbytes).
RK05 fixed disk drive (RK11).
Console TTY (DL11).
Вот так для примера в живую выглядит «RK05 fixed disk drive»:

И создатель этого удивительного проекта смог эмулировать обе этих машины, уложившись в 3636 байт исходного кода на С!
Не буду добавлять классическое «а вам слабо», потому что такое «слабо» и мне и всем моим коллегам и всем знакомым и всем знакомым знакомых, даже живущих в других странах и в той самой Калифорнии.
Но эмуляция столь старого железа — лишь половина работы, другая половина это возможность запуска уникальных исторических операционных систем:
UNIX v0 for the PDP-7 (circa 1969)
Research UNIX Version 6 (circa 1975)
BSD 2.9 (circa 1983)
«Circa» это «примерно», поскольку речь про первые версии, R&D и тестовые сборки, точных релизных дат тут быть не может.
UNIX v0 это случайно найденная и чудом восстановленная с ассемблерных распечаток (!) копия исходного кода операционной системы UNIX. Можно сказать «альфа-версия», пользуясь современной терминологией.
Как выглядит такая распечатка можно посмотреть в ролике ниже.
Вся история с картинками и актуальными ссылками находится тут, а тут выложены восстановленные с таким трудом исходники, вместе с эмулятором.
Этот эмулятор я также успел собрать и запустить:

Учетная запись «ken», используемая для входа — того самого Ken Tompson, оригинального автора UNIX, справа можно увидеть скан оригинальной распечатки с ассемблерным листингом и комментариями, сделанными лично руками Кена Томпсона (!).
Ролик целиком можно посмотреть на нашем Telegram-канале, а вот тут находится также случайно найденный черновик (draft) руководства по UNIX v0:

Но оторвемся на время от изучения исторических артефактов и посмотрим на исходный код сего чуда:
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#include <sys/time.h>
#include <sys/mman.h>
# define L } if(!i -- ){
struct timeval F,G; struct termios H,U={ T} ; enum{ N=64,a=N<<7,b=a-1,c=a*32,d
=c-1, e=c/ 2,f= a*2, g=a/2,h =g/2,j =h/ 2,Q=V*j*5} ; char*s=P,K,M;
int* p, l[ a] ,m,n,J,o=A, O=j,E,R,i,k,t,r,q,u,v,w,x,y,z
,B,C, *D,Z ;int main (){ for(D=mmap(D,4*Q,3,W,open(I,2 ),K); *s; o
++[ l]=k|=* s++%N){ k=* s++%N<<12; k|=*s++% N*N; } tcgetattr(q,& H); tcsetattr
(y,2, &U); for( fcntl(B,4,4); ; o&=b){ if(k& c){ q=- --k%N; if(!q)
k-=c ;i =k /N&7; { L L if(J&1) m+= t; J|=m%N*c; J/=2; m
/= 2; if(! q&&r ^n){ m^=d; J^= d; n=0; } L L J+=J; J|=m>=0; if(q){
m+=m; m|=J/c; m+=m<0?t:-t; } else{ m+=(m<0)*t; if(r)m^=d; if(n^r)J^= d; n=0; }
L if( (m^2 *m)/ e%2) k&=d; else{ J+=J; m+=m; m|=J/c; } L m|=n*c; J
|= m %N *c ;m /=2; J /=2; L m+=m; J+=J; J|=n; m|=J/c; L
m+=m; m|=n ; } J&=d ; } else{ i=k/f; t=i?k&b:16; p=l+t; if(k&a)p=
l+((*p+=13>i&&7<t&&16>t)&b); { L i=1; L*p=m; L*p++=o|n*e; o=p-l; L*p=0; L m=*p
; L m ^=*p ; L t=m; m +=*p; m+=d<m ; n|=((m^t)& (m^*p))/e; L m+=*p
; n=m/c; L k=*p; if( !Z||k/f-8) /*$ %*/ continue ; k=-k
; L++*p; o +=!(*p&=d) ; L m&=*p; L if(m!=*p)++ o; L o=p -l; L if(
k&a)n=m/e; if(k&g)J= 0; r=k&h&&m&e; if(k&j)J|=m; else if(r)m^=d; if(k&512)m=0;
i=k/N &7; { L if(k &4)J ^=d; if(k&2)m|=J; if (k&1 )m|= q; }
else { if(k%N)k += c; { L t =o ++[ l] ; if(r
)J^=d; L L t=o++[l]; if(r )J^=d; m-=t; if(m>=0 ){ k -=c; n=1;
++o; } } i=2; } L if(Z)k=-1; else{ if(k&8)m=0; t=r=0; i=k/N%N; if(i==27){ if(k
&2)u=v=w=Z=0; } if (i==57){ i=k/16&3; { L w =1; if(k&1)x =0; if(k&
2) { t= z/N; t= t/80*/*/*/100+t% 80; r=0 ; while(t) { r
+=t%10*w; t/= 10;w <<=4; } m|=r; } if(k&4){ r=m ; t=0; while (r) {
t+=r%16*w; w*=10; r>>=4; } r=t/100; t%=100; if(V<=r||79<t)x|=c/8; else z=(r*80
+t)*N ; } w=0; L if(k& 1&&x &(e|g) )++o; if(k& 2)m|=x |y;if
(k&4 )C =- m&65535; L if( k&1 )x=y=0; if (k&2
)B=m; if ( k&4) { y^=m&(h|j| j/2); if(y&j ){ y^=j;x|=g ; do{
B&=b; if(y&j/2)z[D]=B[l]; else B[l]=z[D]; ++z; z%=Q; ++B; } while(-- C); } } }
x%=e; if(x /a)x |=e; if(x&(e|g)&&y&h)u|=c ; else u%=c; L if(
k&1) t= h; if (k&2 )r= e; if( k&4){ r=j; u&=~
h ; } if(k &16) Z=f* 2; L L L t=f; if(k&2 )m|=M|Y; if( k&4)m
|=u|v; L t=a; if(k&4){ K=m&~Y; write(1,&K,1); u|=t; t=0; } } i=2; if(t){ if(k&
1&&u&t)++ o; if(k&2)u &=~ t; } if( r){ if(k&32) w=r; else v&=~r; }
} L if(k&a)m=k; else { t=0; if( k & N)t |= m/e%2; if(k&
128)t|=!m; if(k&256)t |= n; if( k& 512 )t=!t; o +=t; if(k& h)n =0;
if(k&g)m=0; if(k&1)m^=d; if(k&2)n^=1; if(k&4)m|=S; if(k&8){ m|=n*c; m+=m; if(k
& j)m +=m; m|=m /a/N ; n=m/c; } if(k&16){ m|=n*c; m|=m *2%N*
c; m /= 2; if (k&j) m/=2; n=m/c; } if (k &32)
{ if( Z)k= -1 ; else break; } } } } n&=1; if(k<c) { m &= d;
o &=b; if(!R--){ if (~u&f &&read(0, &M,1)>0){ if(X&& M== X)break; R=0; u|=f; }
gettimeofday(&G,0); G.tv_usec/=16667; if(G.tv_sec>F.tv_sec||F.tv_usec<G.
tv_usec){ F=G; if(v&j){ p=l+7; ++*p; *p&=d; if(!*p)u|=h; } R=0; } if
(!R){ E=O/4; O=4; } O+=R=E; } if(!++k||(v&e&&u)){ *l=o|n*e|Z;v%=
e; o=1+!k; Z=0; } v|=w; w=0; k=o++[l]; } }tcsetattr(w,1,&H); }
Для юного поколения разработчиков это не очевидно, но оформление кода тут стилизовано под перфокарту, отсюда и такие визуальные «дырки»:
The ASCII art represents a torn piece of paper tape.
И да, это весь исходный код, обеспечивающий запуск аж трех исторических ОС на эмулируемом железе двух разных компьютеров из 1980х: PDP-7 и PDP-11.
Сборка
Сборка и запуск не представляют проблемы в современном окружении поскольку никаких внешних зависимостей у проекта нет а интерфейс целиком консольный.
Автор собирал на FreeBSD 14 с помощью GCC 13, так что на любом Linux точно соберется без проблем.
Все проекты-победители IOCCC находятся в одном репозитории на Github, поэтому забирать придется также все целиком:
git clone https://github.com/ioccc-src/winner.git
Нужный проект находится в каталоге 2018/mills
.
В качестве альтернативы можно скачать архив со страницы на сайте IOCCC, посвященной этой работе (ссылки в самом низу).
В случае сборки под FreeBSD необходимо поправить в файле var.mk название команды для запуска GCC:
GCC=gcc13
Поскольку в FreeBSD gcc не является системным компилятором и устанавливается с постфиксом версии.
Сама сборка осуществляется с помощью GNU Make:
gmake
После выполнения этой команды, в каталоге проекта появятся бинарники prog
, v0
и v6
, которые мы и будем запускать:

Три разных бинарника на самом деле одно и то же приложение, собираемое с разными константами и разными загрузочными образами, подставляемыми в код при сборке.
UNIX v0
Собственно для запуска эмулятора PDP-7 с нулевой версией Unix необходимо выполнить:
./v0
Через некоторое время появится приглашение авторизации, где надо ввести dmr
в качестве логина и пароля.
Да, это учетка того самого Dennis Ritchie, одного из авторов UNIX, чью подпись вы могли наблюдать выше на скане черновика руководства.

Все дальнейшие шаги по компиляции классического «Hello World» на предке С — языке B в нулевой версии UNIX можно увидеть в записи ниже:

Для сравнения и атмосферности, покажу как это выглядело на настоящем PDP-7:

Внезапно всплывает еще один интересный нюанс:
Thompson’s assembler was also extremely simple. It didn’t even know about opcodes – these need to be defined in the first assembly file, with the assembler OR-ing the opcode fields together (space was the “operator” for logical OR). There is no link step – all files are provided to the assembler on the command line, concatenated together and assembled, producing a single
a.out
(assembler output) file. Even after UNIX got a linker,a.out
remained the default name of the binary generated by the linker (and hence also bycc
).
Так что теперь вы тоже знаете насколько же далеко в прошлое уходят уши названия a.out
, используемого компиляторами и по ныне.
Стоит также рассказать про еще один важный нюанс, специфичный для Unix v0:
ls
needs to be able to read.
, the current directory.dmr
’s home directory doesn’t have that yet, but we can make it, because the home directory has a hard link todd
, which is the directory that holds all of the user home directories (this will eventually become/
, the root path).
А все потому, что каталоги.. еще не были изобретены:
What’s the deal here? Well, that’s a slightly long story. The short version is we have gone so far back in the history of UNIX that we don’t have filesystem paths yet.
Надо выполнить команду:
ln dd dmr .
для того чтобы команда ls
могла отрабатывать.
Но едем дальше.
UNIX v6
Важная веха в истории развития Unix:
Sixth Edition Unix, also called Version 6 Unix or just V6 is a version of the Unix operating system first released in May 1975 and the first version of the Unix operating system to see wide release outside Bell Labs.
Поскольку это первая версия, получившая широкое распространение за стенами лаборатории Bell:
An enhanced V6 was the basis of the first ever commercially sold Unix version, INTERACTIVE's IS/1. Whitesmiths produced and marketed a (binary-compatible) V6 clone under the name Idris.
Настолько широкое, что проникла даже за «железный занавес» в СССР:
The system is derived from Version 6 Unix and then modified to incorporate features of Berkeley Software Distribution (BSD) Unix. From 1983 until 1986, it enjoyed popularity in the Soviet Union and other Eastern Bloc countries, due to its small size and faster performance than that of other Version 7 Unix (and later BSD Unix-based) alternatives.
Что характерно это даже не широко известный ДЕМОС а нечто под названием МОС (Мобильная Операционная Система), о которой я никогда даже не слышал.
Для запуска эмулятора с Unix v6 делаем очевидное:
./v6
Получаем командную строку загрузчика, в котором пишем:
rkunix
Пойдет загрузка Unix v6 в однопользовательском режиме:
This version of UNIX doesn’t go into multi-user mode if you do a Control-D. Single-user mode was entered because the front-panel console switches were set to the magic number 0173030 (this can be changed with a recompile).
Те самые «front‑panel switches» выглядели вот так:

Но это действо (менять magic number ради полноценного multi-user) я уже не пробовал.
К сожалению образ Unix v6 довольно-таки пустой и примеров исходного кода в нем нет, а компилятор... скажем так очень специфичный:
The
short
data type doesn’t exist yet. Onlyint
(short
will show up whenlong
comes, and the port to the Interdata 7/32 starts to make the idea of portability become important).Hexadecimal constants don’t exist yet. Digital’s computers all used octal.
The array initializer syntax hasn’t yet moved to using
=
. It still uses the older form taken from B, which looks likearray[] { 1, 2, 3 };
Собрать даже «Hello world» этим компилятором я не осилил, хотя судя по описанию все должно быть довольно просто:
# cat > foo.c
main()
{
printf("Hello, World!\n");
}
^D
# cc foo.c
# ./a.out
Hello, World!
Поэтому осталось только лазить по файловой системе и умиляться историческим датам на файлах.
В записи это выглядит так:

Как можно заметить по ролику, тут есть интересный нюанс в виде использования символов верхнего регистра для вывода, чтобы это отключить надо ввести команду:
STTY -LCASE
BSD UNIX v2.9
Наконец главная часть этой шикарной работы, для чего собственно все и затевалось:
..and enjoy the classic 1984/mullender entry as it would have been in 1984 with a VAX-11/PDP-11!
Да, симулятор позволяет запустить первую работу-победителя IOCCC с самого первого проведения этого конкурса в 1984м году (!)
Которая внезапно была предназначена для запуска отнюдь не на PC:
This original code will only execute correctly if your machine is a VAX-11 or PDP-11.
На всякий случай покажу код этой работы, если вы вдруг не видели сей шедевр:
short main[] = {
277, 04735, -4129, 25, 0, 477, 1019, 0xbef, 0, 12800,
-113, 21119, 0x52d7, -1006, -7151, 0, 0x4bc, 020004,
14880, 10541, 2056, 04010, 4548, 3044, -6716, 0x9,
4407, 6, 5568, 1, -30460, 0, 0x9, 5570, 512, -30419,
0x7e82, 0760, 6, 0, 4, 02400, 15, 0, 4, 1280, 4, 0,
4, 0, 0, 0, 0x8, 0, 4, 0, ',', 0, 12, 0, 4, 0, '#',
0, 020, 0, 4, 0, 30, 0, 026, 0, 0x6176, 120, 25712,
'p', 072163, 'r', 29303, 29801, 'e'
};
Да это тоже язык Си и нет, это не получится собрать и запустить современным компилятором на обычном PC.
Можете юмора ради попросить ChatGPT или Copilot подсказать почему такой код не работает.
Вся последовательность действий от запуска эмулятора до компиляции и старта знаменитого демо из 1984го года выглядит так:

Эпилог
Эта чудовищной мощи работа — живой памятник инженерному делу и мастерству программирования, надеюсь она поможет расширить понимание возможностей эмуляции, а также увидеть своими глазами частичку истории ИТ и компьютеров.
Процитирую напоследок авторский список благодарностей, без которых эта удивительная работа никогда бы не появилась на свет:
None of this could have been possible without the hard work of
Warren Toomey and the other members of the The Unix Heritage Society
Robert M. Supnik and the other contributors to SimH, the simulator for historic computer architectures. The number of times I needed to “Use the Source, Luke” on SimH to unravel some dark corner of these machines was uncountable.
Bitsavers, which acquired, scanned and preserved all the manuals I spent many hours squinting at.
Ken Thompson, Dennis Ritchie, Brian Kernigan, M. D. McIlroy, J. F. Osssanna, Rudd Canaday and the other members of the Bell Labs Computing Science Research Center who were responsible for the invention of UNIX, the C programming language, and the innumerable other innovations that we now take for granted as part of the modern software landscape. In particular, the home page of the late Dennis Ritchie contained a trove of useful information on the evolution of UNIX and C and is recommended for perusal by others who share my peculiar retro computing passion.
Оригинал статьи как обычно в нашем блоге, еще мы начинаем писать видео, где со временем постараемся раскрывать интересные ИТ-темы в.. более медийном стиле ;)
arteast
Автор этого удивительного проекта смог эмулировать одну эту машину PDP-7, уложившись в 3636 байт. Эмулятор PDP-11 написан на машкоде PDP-7 и лежит в дисковом образе.