vak: (бэсм-6)
(Я послал этот текст в список рассылки БЭСМ-6, но сохраню и здесь для памяти.)

Раз уж у нас тут постепенно актуализировалась тема написания компиляторов, я тоже увлёкся и реализовал давнишнюю мечту: портонул Би с PDP-7 на БЭСМ-6.

Исходник компилятора: b-besm.b

Проект на Гитхабе: besm6/b-compiler

История вопроса следующая. В 1969 году Кен Томпсон создал язык Би для машины PDP-7. Компилятор порождал так называемый шитый код, который интерпретировался во время выполнения. Позже в 1971-м Деннис Ритчи портонул Би на машину Honeywell GE 645, но без всякого шитого кода, а уже с нормальной генерацией бинарного кода целевого процессора. А когда Ритчи взялся тащить Би на PDP-11, оказалось, что наличие байтовой адресации существенно меняет дело. Пришлось добавить в Би типы, и он быстро превратился в знакомый нам Си.

Исходники компилятора Би считались потерянными. Но девять лет назад Robert Swierczek чудесным образом сумел восстановить тексты Би из обрывков распечаток в рамках проекта pdp7-unix.

Я взял эти исходники и вместо выдачи шитого кода для PDP-7 вставил генерацию машинных команд БЭСМ-6 для ассемблера Madlen. Добавил минимальную рантайм библиотеку и запихнул в мониторную систему Дубна. Компилятор пишет ассемблерный текст на барабан и, если не случилось ошибок, выполняет как бы команду *READ:1. Дальше обычным образом стартует Мадлен и формирует объектные модули. Компилятор Би и рантайм библиотеку я поместил на отдельную ленту, её можно подключать командой *TAPE:7/B. Покажу пример на симуляторе dubna.

Вот скрипт с исходным текстом классического примера Hello World. Компилятор запускается с ленты командой *TRANS.
*name B compiler
*tape:7/b,40
*library:40
*trans-main:40020
main() {
printf("Hello, B!*n");
}
*execute
*end file
Запускаем. Я не стал убирать таблицу загрузки, чтобы вам было видно размер кода:
$ dubna hello.dub
...
*NAME B COMPILER
*TAPE:7/*,40
*LIBRARY:40
*TRANS-MAIN:40020
OVERLAY OT 20/04/25
B COMPILER FOR BESM-6, VERSION 04/2025
COMPILED 3 LINES OF CODE, FOUND 0 ERRORS

*NO LIST
*CALL BLOCKERR
*MADLEN
MAIN CP 0.0, ST 0.0, RT 1.0*
*READ OLD
*EXECUTE
*LIBRA:40 = B/DIV B/EQ B/GT B/LT B/MUL B/NE B/RET B/SAVE0 B/SAVE
B/TOUT B/TRUE CHAR PRINTD PRINTO PRINTF WRITE WRITEB OUT*CNT OUT*SHFT
OUT*BUFF FLUSH FOUT

MAIN 01000 PRINTD 01413 B/MUL 01757 ISOTCOSY 06002
PROGRAM E 01000 PRINTO 01466 B/GT 01766 ISOTCOS1 E 06053
B/SAVE0 01011 WRITE 01517 FOUT 01771 DRUMTAP* 06073
B/RET 01016 B/TRUE 01622 WRCARD 01772 CHKWORD* C 06124
PRINTF 01023 OUT*CNT 01623 WRWORD E 02014 PRINT8 06125
B/SAVE 01256 OUT*SHFT 01624 WBEGIN E 02033 STOP* 06306
CHAR 01263 OUT*BUFF 01625 WRIEND E 02042 EXIT E 06307
B/NE 01301 FLUSH 01654 B/TOUT 02075 CBOБOДHO 06335
B/EQ 01304 B/LT 01744 WRWORD/ C 04000
WRITEB 01307 B/DIV 01747 XWRITE/ C 06000

HELLO, B!
Вот ещё несколько примеров для Би из разных мест. Все они работают под Дубной:Мандельброт выглядит забавно: mandelbrot.txt

Выводы из всей этой затеи:
  • Би вполне был возможен на БЭСМ-6 уже тогда, в середине 70-х. Увы, никто из аксакалов-бэсмачей про него не знал.
  • Если бы Би тогда состоялся, он мог бы стать важным средством системной разработки. Не судьба. Позже язык ЯРМО пытался занять нишу.
  • Результат примерно соответствует мечте Игоря Григорьевича Пасынкова, когда в 1986 году он позвал меня в курчатник заниматься Си для Эльбруса-Б. Ему хотелось видеть Си не только под юниксом, но и в мониторке.
vak: (Default)
Вот такая програмулина:
main() {
auto cx, cy, x, y, x2, y2;
auto iter;
auto xmin, xmax, ymin, ymax, maxiter, dx, dy;

xmin = -8601;
xmax = 2867;
ymin = -4915;
ymax = 4915;

maxiter = 32;

dx = (xmax - xmin) / 79;
dy = (ymax - ymin) / 24;

cy = ymin;
while (cy <= ymax) {
cx = xmin;
while (cx <= xmax) {
x = 0;
y = 0;
x2 = 0;
y2 = 0;
iter = 0;
while (iter < maxiter) {
if (x2 + y2 > 16384)
goto next;

y = ((x * y) / 2048) + cy;
x = x2 - y2 + cx;
x2 = (x * x) / 4096;
y2 = (y * y) / 4096;
iter++;
}
next:
write(' ' + iter);
cx = cx + dx;
}
write('*n');
cy = cy + dy;
}
return(0);
}
Запускаем mandelbrot.dub, получаем mandelbrot.txt:
$ dubna mandelbrot.dub
...
!!!!!!!!!!!!!!!"""""""""""""####################################""""""""""""""""
!!!!!!!!!!!!!"""""""""#######################$$$$$$$%'+)%%%$$$$$#####"""""""""""
!!!!!!!!!!!"""""""#######################$$$$$$$$%%%&&(+,)++&%$$$$$$######""""""
!!!!!!!!!"""""#######################$$$$$$$$$$%%%%&')*5:/+('&%%$$$$$$#######"""
!!!!!!!!""""#####################$$$$$$$$$$%%%&&&''),@@@@@@@,'&%%%%%$$$$########
!!!!!!!"""####################$$$$$$$$%%%&'())((())*,@@@@@@/+))('&&&&)'%$$######
!!!!!!""###################$$$$$%%%%%%&&&'+.@@=/<@@@@@@@@@@@@@@@/++@..93%%$#####
!!!!!"################$$$%%%%%%%%%%&&&&'),+2@@@@@@@@@@@@@@@@@@@@@@@@@1(&&%$$####
!!!!"##########$$$$$%%&(-(''''''''''''(*,5@@@@@@@@@@@@@@@@@@@@@@@@@@@@+)-&%$$###
!!!!####$$$$$$$$%%%%%&'(*-@1.+.@-4+))**@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@4-(&%$$$##
!!!!#$$$$$$$$$%%%%%%'''++.6@@@@@@@@@8/0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@3(%%$$$$#
!!!#$$$$$$$%&&&&''()/-5.5@@@@@@@@@@@@@>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?'&%%$$$$#
!!!(**+/+<523/80/46@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@4+)'&&%%$$$$#
!!!#$$$$$$$%&&&&''().-2.@@@@@@@@@@@@@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'&%%$$$$#
!!!!#$$$$$$$$$%%%%%&'''/,.7@@@@@@@@@;/0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@0'%%$$$$#
!!!!####$$$$$$$$%%%%%&'(*-:2.,/?-5+))**@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@4+(&%$$$##
!!!!"##########$$$$$%%&(-(''''(''''''((*,4@@@@@@@@@@@@@@@@@@@@@@@@@@@4+).&%$$###
!!!!!"################$$$%%%%%%%%%%&&&&')<,4@@@@@@@@@@@@@@@@@@@@@@@@@/('&%%$####
!!!!!!""##################$$$$$$%%%%%%&&&'*.@@@0@@@@@@@@@@@@@@@@1,,@//9)%%$#####
!!!!!!!"""####################$$$$$$$$%%%&(())((()**-@@@@@@/+)))'&&&')'%$$######
!!!!!!!!""""#####################$$$$$$$$$$%%%&&&''(,@@@@@@@+'&&%%%%%$$$########
!!!!!!!!!"""""#######################$$$$$$$$$$%%%%&')*7@0+('&%%%$$$$$#######"""
!!!!!!!!!!!"""""""######################$$$$$$$$$%%%&&(+-).*&%$$$$$$######""""""
!!!!!!!!!!!!!"""""""""#######################$$$$$$%%'3(%%%$$$$$######""""""""""
!!!!!!!!!!!!!!!""""""""""""#####################################""""""""""""""""
vak: (Default)
Я затащил компилятор Би под мониторную систему Дубна. Сделал отдельную ленточку 7/b, на которой лежит бинарник компилятора и рантайм библиотека. Вот классический пример Hello World:
*name B compiler
*tape:7/b,40
*library:40
*trans-main:40020
main() {
printf("Hello, B!*n");
}
*execute
*end file
Запускаем:
$ dubna b.dub
...
*NAME B COMPILER
*TAPE:7/*,40
*LIBRARY:40
*TRANS-MAIN:40020
OVERLAY OT 20/04/25
B COMPILER FOR BESM-6, VERSION 04/2025
COMPILED 3 LINES OF CODE, FOUND 0 ERRORS

*NO LIST
*CALL BLOCKERR
*MADLEN
MAIN CP 0.0, ST 0.0, RT 1.0*
*READ OLD
*EXECUTE
*LIBRA:40 = B/DIV B/EQ B/GT B/LT B/MUL B/NE B/RET B/SAVE0 B/SAVE
B/TOUT B/TRUE CHAR PRINTD PRINTO PRINTF WRITE WRITEB OUT*CNT OUT*SHFT
OUT*BUFF FLUSH FOUT

MAIN 01000 PRINTD 01413 B/MUL 01757 ISOTCOSY 06002
PROGRAM E 01000 PRINTO 01466 B/GT 01766 ISOTCOS1 E 06053
B/SAVE0 01011 WRITE 01517 FOUT 01771 DRUMTAP* 06073
B/RET 01016 B/TRUE 01622 WRCARD 01772 CHKWORD* C 06124
PRINTF 01023 OUT*CNT 01623 WRWORD E 02014 PRINT8 06125
B/SAVE 01256 OUT*SHFT 01624 WBEGIN E 02033 STOP* 06306
CHAR 01263 OUT*BUFF 01625 WRIEND E 02042 EXIT E 06307
B/NE 01301 FLUSH 01654 B/TOUT 02075 CBOБOДHO 06335
B/EQ 01304 B/LT 01744 WRWORD/ C 04000
WRITEB 01307 B/DIV 01747 XWRITE/ C 06000

HELLO, B!
Компилятор генерит ассемблерный код и пишет его на барабан, то есть временный файл фактически. И переключает мониторку на чтение этого барабана, как бы командой *READ:1. Вызывается Мадлен и превращает ассемблерный код в бинарные объектные модули.
vak: (бэсм-6)
Переписал некоторые функции рантайм библиотеки с ассемблера на Би. К примеру, было:
    writeb: ,name,
         12 ,base,*
c
c Append one byte to output buffer.
c
 b/output:lc,block, w_count, b_shift, buffer(22)
c
    flush:  ,subp,
            ,aax, =377
            ,aex, =12       . compare to linefeed
            ,uza, flush
            ,aex, =12
c
            ,utc, w_count
            ,xts,
            ,aex, =26       . 22 words
            ,u1a, putchar
            ,ita, 13
            ,its,
            ,call, flush
            ,sti,
            ,ati, 13
c
 putchar:   ,bss,           . append byte to buffer
            ,sti,
         11 ,vtm, b/output
         11 ,wtc, 1         . b_shift
            ,asn, 64-40     . shift left
         11 ,wtc, 0         . w_count
         11 ,aox, 2         . read word from buffer
         11 ,wtc, 0         . w_count
         11 ,atx, 2         . write word to buffer
c
         11 ,xta, 1         . b_shift
            ,aex, =50
            ,uza, next_w
            ,aex, =50
            ,arx, =10       . increase shift
         11 ,atx, 1         . b_shift
         13 ,uj,
c
 next_w:    ,bss,           . advance to next word
         11 ,atx, 1         . clear b_shift
         11 ,xta, 0         . w_count
            ,arx, =1        . increment word count
         11 ,atx, 0         . w_count
         13 ,uj,
            ,end,
Стало:
/*
 * Append one byte to output buffer.
 */
writeb(b) {
    extrn out_cnt, out_shft, out_buff;
    auto p;

    b = b & 0377;
    if (b == '*n') {
        flush();
        return;
    }

    if (out_cnt == 22) {
        flush();
    }

    p = &out_buff[out_cnt];
    *p = *p | (b << (40 - out_shft));

    if (out_shft == 40) {
        /* next word */
        out_shft = 0;
        ++out_cnt;
    } else {
        /* next byte */
        out_shft = out_shft + 8;
    }
}

out_cnt;
out_shft;
out_buff[22];
Так ведь понятнее выходит, правда?
vak: (Default)
Компилятор Би, будучи запущенным на БЭСМ-6, успешно обработал собственный исходный текст и выдал разумный ассемблерный код. До этого я гонял его в кросс-режиме под Линуксом. Но теперь уже и на самой БЭСМ-6 можно. С перекодировкой пришлось помучиться.

Сравниваю ассемблерный код, полученный в кросс-режиме и на БЭСМ. Вижу расхождение в одной строке.
$ diff bbesm.madlen bbesm2.madlen 
5185c5185
< ,XTS,=37777777777777
---
> ,XTS,=50000000000001
Это код функции negate().
negate(n) {
return (-n & 037777777777777);
}
Почему-то на БЭСМ получилась неправильная целочисленная константа. Надо искать багу в арифметике.
vak: (Default)
Простая программа: копирует входные байты на выход.
main() {
auto ch;

while (ch = read()) {
write(ch);
}
}
Пропускаем через Би компилятор, получаем ассемблерный код.
$ bbesm < cat.b > cat.madlen
$ cat cat.madlen
main:8,name,
program:,entry,
main:,bss,
b/save0:,subp,
b/ret:,subp,
,its,13
,call,b/save0
15,utm,1
/0:,bss,
7,utc,0
14,vtm,
,ita,14
read:,subp,
14,vtm,read
,its,14
,ati,14
14,utc,
13,vjm,
15,wtc,
,atx,
,uza,/1
write:,subp,
14,vtm,write
,ita,14
7,utc,0
14,vtm,
,its,14
,ati,14
14,xta,
14,vtm,-1
15,wtc,
13,vjm,
,uj,/0
/1:,bss,
,uj, b/ret
,end,
Компилируем в бинарник для БЭСМ-6. Линкуем с рантайм библиотекой Би.
$ besmc cat.madlen libb.obj
Запустим и подадим на вход собственный исходный код:
$ ./cat.exe < cat.b 
MAIN() [
AUTO CH;

WHILE (CH = READ()) [
WRITE(CH);
]
]
Всё перекодировалось в КОИ-7 по ходу. Это специфика мониторной системы Дубна, никуда не денешься. Ведь мы используем её механизм формирования входного потока программы.
vak: (Default)
Компилятор Би успешно скомпилировал сам себя и загрузился в память БЭСМ-6. Занимает 5270 слов памяти, то есть примерно 31 килобайт.

Вот таблица загрузки:
         MAIN       01000                B/SAVE     06222
PROGRAM E 01000 NAME 06226
B/SAVE0 01022 GEN*SUBP 06266
B/RET 01026 GEN*RVAL 06313
SYMTAB 01032 WRITEB 06350
EOF 02163 PRINTN 06376
NS 02164 NERROR 06432
B/NOT 02165 PRINTF 06433
EXTDEF 02170 ACC*ACTI 06702
BLKEND 02760 EXPR 06703
B/TRUE 02771 GEN*GOTO 10373
PEEKSYM 02772 PEXPR 10430
CSYM 02773 JUMPC 10466
CVAL 02774 JUMP 10511
NAUTO 02775 LABEL 10534
NPARAM 02776 READ* 10557
RETFLAG 02777 MAPCH 10605
SYMBOL 03000 IS*LVALU 10764
B/EQ 03632 B/OUTPUT C 10765
B/NE 03635 FLUSH 11015
GEN*PROL 03640 B/DIV 11031
GEN*ENTR 03700 B/MOD 11041
DECLARE 04013 CHAR 11054
GEN*BSAV 04215 PRINTD 11072
B/LT 04331 GEN*CONS 11144
B/GT 04334 GEN*STRI 11206
GEN*STAC 04337 GEN*AUTO 11264
STMTLIST 04362 GEN*PARA 11351
GEN*BRET 04422 GEN*EXTR 11436
GEN*EPIL 04443 GEN*INTE 11515
NEGATE 04461 GEN*HELP 11573
WRITE 04470 ASSERT*L 11622
PRINTO 04503 GEN*ASSI 11654
NUMBER 04532 GEN*LSHI 11676
ERROR 04561 GEN*LSHA 11727
STMT 04620 GEN*RSHI 11774
ISN 05340 GEN*RSHA 12025
SYMBUF 05341 GEN*MCAL 12057
CTAB 05354 GEN*CALL 12123
PEEKC 05555 MONREAD* 12240
LINE 05556 READ*ERR E 12320
B/GE 05557 PRINT8 12344
READ 05562 STOP* 12525
SUBSEQ 05567 GETSTR 12554
B/LE 05620 DRUMTAP* 13120
B/MUL 05623 COSYTISO 13151
GETCC 05632 CHKWORD* C 13225
LOOKUP 06007 CBOБOДHO 13226
vak: (Default)
Компилятор Би для БЭСМ-6 функционально закончен. То есть все фичи, в нём имеющиеся, вроде работают. Некоторые привычные для нас вещи отсутствуют: инкремент-декремент, сдвиги, операторы типа плюс-присвоить. Нет цикла for и оператора switch. Не поместились они у Кена Томпсона в 16 килобайт памяти PDP-7.

Сгенерённый компилятором код получается неэффективный, но для нас сейчас главное, что он функционально правильный. А насколько он неэффективный - давайте померяем. Возьмём для теста известный алгоритм вычисления e-2 по формуле 1/2! + 1/3! и так далее. Вот реализация на Би: e-2.b. Компилируем, запускаем:
$ bbesm < e-2.b > e-2.madlen
$ besmc e-2.madlen libb.obj
$ time ./e-2.exe
71828 18284 59045 23536 02874 71352 66249 77572 47093 69995
...
88683 31483 28027 06553 83300 46932 90115 74414 75631 39997

real 0m18.310s
user 0m18.302s
sys 0m0.008s
Для сравнения я переписал этот код на Паскаль: e-2.dub. Запустим на том же симуляторе:
$ dubna e-2.dub
...

71828 18284 59045 23536 02874 71352 66249 77572 47093 69995
...
88683 31483 28027 06553 83300 46932 90115 74414 75631 39997
------------------------------------------------------------
Elapsed time: 12.3 seconds
Simulated: 626677681 instructions
Simulation rate: 51010913 instructions/sec
Выходит 18.3 секунды против 12.3 секунды на Паскале. Не так ужасно, всего в полтора раза медленнее. С оптимизацией Би может оказаться даже эффективнее Паскаля.
vak: (Default)
Компилятор Би для БЭСМ-6 маленько подрос и уже способен правильно сгенерить функцию printf(). Вот как она выглядела в районе 1969 года.
printf(fmt, args) {
auto ap, a, c, i, n;

i = 0;
ap = &args;
loop:
while ((c = char(fmt, i)) != '%') {
if (c == '*0')
return;
writeb(c);
i = i + 1;
}
i = i + 1;
c = char(fmt, i);
if (c == '%') {
writeb('%');
i = i + 1;
goto loop;
}
a = *ap;
if (c == 'd') {
printd(a);
} else if (c == 'o') {
printo(a);
} else if (c == 'c') {
write(a);
} else if (c == 's') {
n = 0;
while ((c = char(a, n)) != '*0') {
writeb(c);
n = n + 1;
}
} else {
/* bad format specification, ignore */
goto loop;
}
i = i + 1;
ap = ap + 1;
goto loop;
}
Напишем тест, задействуя все форматы.
main() {
printf("Hello, World!*n");
printf("%% %% %%%%*n");
printf("format %%d: %d %d*n", 123, -123);
printf("format %%o: %o %o*n", 234, -234);
printf("format %%c: %c %c*n", 'foo', 'bar');
printf("format %%s: *"%s*" *"%s*"*n", "Hello", "World");
}
Компилируем, запускаем.
$ bbesm < test.b > test.assem 
$ besmc test.assem libb.obj
$ ./test.exe
Hello, World!
% % %%
format %d: 123 -123
format %o: 352 -352
format %c: foo bar
format %s: "Hello" "World"
Фунциклирует в лучшем виде однако.
vak: (бэсм-6)
Пока что для Би на БЭСМ-6 я пришёл к следующему соглашению о связях. Вызов фукнции:
    result = foobar(a, b, c)
транслируется как:
       ,xta, a
       ,xts, b
       ,xts, c
    14 ,vtm, 3
    13 ,vjm, foobar
       ,atx, result
Заметьте: на регистре 14 передаётся количество аргументов. Это нужно, чтобы функции с переменным количеством аргументов могли правильно отыскать в стеке свой первый параметр. Иначе printf() не сможет работать.

Получается односторонняя совместимость с Фортраном. Из Би можно будет вызывать фортрановские функции. Но не наоборот.
vak: (Default)
Компилятор языка Би для БЭСМ-6 подаёт первые признаки жизни. В частности, умеет компилировать пустую программу.

Возьмём пустую программу на Би.
$ cat empty.b
main() {
/* empty */
}
Компилируем в автокод Мадлен:
$ bbesm < empty.b > empty.madlen
$ cat empty.madlen
main:,name,
program:,entry,
,its,7
,its,13
,its,
15,mtj,7
7,mtj,15
7,stx,-3
,sti,13
,sti,7
13,uj,
,end,
Компилируем в бэсмовский бинарник и запускаем:
$ besmc empty.madlen
$ ./empty.exe
$ _
Программа выдаёт ничего, как и положено.
vak: (Default)
Вспоминаю молодость. Распечатал бэсмовские листинги, изучаю тонкости работы мониторной системы "Дубна".

vak: (Default)
Я тут подумал. Если взглянуть на взаимодействие юзерского кода с ядром юникса, можно заметить, что системы команд не обязаны совпадать. Программа юзера может иметь одну систему команд, а ядро - совсем другую. Параметры системного вызова передаются через регистры. Если форматы данных и указателей более-менее совместимы, то и нет проблем.

Мы имеем мэсм6, то есть реализацию системы команд БЭСМ-6 на Верилоге. А Микрочип имеет PolarFire: микропроцессор RISC-V с Линуксом и FPGA в одном чипе. Платка стоит $132 на microchipdirect.com.
Что если скрестить ежа с ужом? Добавить к имеющимся ядрам RISC-V еще и ядро БЭСМ-6. Благо FPGA вроде немаленького размера (95K логических элементов). Научить Линукс запускать и обслуживать процесс БЭСМ-6. Задействовать имеющийся у нас компилятор Паскаля, и можно будет кодить для БЭСМ-6 под Линуксом!

А если ещё маленько поднапрячься, можно и мониторную систему "Дубна" подтащить. Получим весь спектр языков программирования и ассемблеров. Опыт мы наработали на симуляторе dubna.

Мне кажется, вполне реальная идея.

vak: (Знайка)
Наш эмулятор БЭСМ-6 развивается уже около 30 лет. За это время мы гоняли на нём кучу разного софта. Вдохновлённые успехами, мы верили, что уж систему команд-то мы поняли хорошо и реализовали качественно. И тут внезапно нежданчик. Вчера обнаружили ошибку в арифметике. Не в самих вычислениях, а в команде выдачи младшей половины результата арифметической операции. Расскажу по порядку.

Эмулятор БЭСМ-6 когда-то начинался с простой необходимости проверять качество кода, порождаемого Си-компилятором. Через некоторое время он развился до dispak, на котором уже можно запустить любую задачу режима пользователя. Если знаете, есть в линуксе такой Wine, на котором можно запускать приложения Windows. Здесь то же самое, только вместо системных вызовов win32 эмулируются экстракоды ОС Диспак.

Пятнадцать лет назад мы раздобыли образ системного диска реальной БЭСМ-6 и соорудили для его оживления более серьёзный эмулятор на основе SIMH. Здесь уже имитируется работа всей периферийной аппаратуры машины, в дополнение к процессору. Прогнали на этих эмуляторах вагон разных компиляторов, прикладных программ и утилит. Все выловленные баги аккуратно исправили до состояния приятной стабильности. Приделали графическую панель, чтобы наблюдать лампочки на пульте и знаменитое диспаковское "жду" на регистрах, когда операционке заняться нечем.



В прошлом году мы доросли до понимания устройства другой бэсмовской операционки, а именно ОС Дубна, и сваяли третий эмулятор dubna. Он работает по той же идее, что и dispak: выполняет системные вызовы и позволяет запускать задания точно в виде, описанном в исторической книжке Мазного "Программирование на БЭСМ-6 в системе Дубна". И тоже всё у нас отлично работало - пока мы не наткнулись на проблему со строками в Алголе.

В составе мониторной системы Дубна присутствует транслятор с языка Алгол-60. Его назвали Алгол-ГДР, чтобы отличать от другой реализации Алгол-БЭСМ, которая имелась только под Диспаком. Так вот, в Алголе-ГДР имелось несколько полезных расширений языка, по сравнению со стандартом Алгола-60. Одно из расширений - тип string. Память под строки автоматически выделяется в особом буфере, и в целом с ними довольно удобно работать. Все операции с алгольными строками отлично функционируют под эмулятором dispak. Но под dubna мы столкнулись с проблемой: элементарная операция индексации (вида s[5]) или среза (s[3:7]) приводила к останову по арифметическому переполнению. Простая программа:
*name Алгол
*no list
*no load list
*algol
'begin'
'string' s;
s := ''abra cadabra'';
print(s, newline);
print(s[3], newline);
print(s[2:8], newline);
'end''eop'
*execute
*end file
Запускаем на одном эмуляторе:
$ dispak algol.b6 
26.07.24 М1

М О Н И Т О Р-8 0 (3.7) 25.09.84* ( МС " Д У Б Н А " ИАЭ ) ШИФР 419999000000
...
*NАМЕ АЛГОЛ
*NО LISТ
*NО LОАD LISТ
*АLGОL
*ЕХЕСUТЕ
...
АВRА САDАВRА
R
ВRА САD
Запускаем на другом:
$ dubna algol.dub 
27 ИЮЛ 24 00.00
ЙOKCEЛ БЭCM-6/5 ШИФP-12
MOHИTOPHAЯ CИCTEMA ′Д Y Б H A′ - 20/10/88
...
*NAME AЛГOЛ
*NO LIST
*NO LOAD LIST
*ALGOL
PROGRAM ДЛИHA: 28 00034B BPEMЯ: 0.00 CEK. KAPT: 7
*EXECUTE
...
ABRA CADABRA
Error: Arithmetic overflow @01467
Что такое, в чём дело? Несколько дней ходили вокруг этой ошибки. Склонялись уже к тому, что в Дубне был сломанный Алгол. Что никто ним не пользовался, а тем более странными строками. Однако взялись копнуть глубже.

Сильная сторона эмулятора - возможность включить подробную трассировку. Включаем, смотрим где упало.
$ dubna -d remi algol.dub
...
01463 L: 14 005 0424 a-x 424(14)
Memory Read [02063] = 6400 0000 0000 0001
ACC = 6400 0000 0000 0002
RAU = 23
01463 R: 00 27 02046 u1a 2046
RMR = 6400 0000 0000 0002
01464 L: 14 017 0423 a*x 423(14)
Memory Read [02062] = 4002 5252 5252 5253
ACC = 6400 0000 0000 0000
RMR = 6405 2525 2525 2526
RAU = 13
01464 R: 17 000 0000 atx (17)
Memory Write [03504] = 6400 0000 0000 0000
M17 = 03505
01465 L: 00 037 0007 ntr 7
RAU = 07
01465 R: 00 031 0000 yta
ACC = 6405 2525 2525 2526
01466 L: 14 015 0414 aox 414(14)
Memory Read [02053] = 4000 0000 0000 0000
RMR = 0000 0000 0000 0000
01466 R: 14 011 0431 aax 431(14)
Memory Read [02070] = 7777 7777 7770 0000
ACC = 6405 2525 2520 0000
01467 L: 14 017 0420 a*x 420(14)
Memory Read [02057] = 6437 7777 7777 7772
Error: Arithmetic overflow @01467
ACC = 1037 7777 7777 7776
RMR = 0000 0000 0040 0000
00020 L: 00 074 0000 *74
Видим, что переполнение случилось в результате команды A*X, то есть умножения. Одно целое число 6405 2525 2520 0000 (слово в восьмеричном виде) умножалось на другое число 6437 7777 7777 7772. То, что числа целые, видно по характерному порядку 64 в старших битах. Режим арифметического устройства установлен в RAU=07, что означает отключение нормализации и округления. Без нормализации порядок 64 воспринимается как 240, то есть одно очень большое число умножается на другое очень большое число. Естественно, переполнение. Так не должно быть.

Откуда взялся порядок 64 у числа на сумматоре? Оно извлечено командой YTA из регистра младших разрядов. А образовалось это значение RMR в результате предыдущей команды умножения A*X. Причём позже на это значение командой побитового "или" (AOX) накладывается значение 4000 0000 0000 0000. То есть делается попытка восстановить биты порядка. И тут закралось первое подозрение. Очевидно, что команда AOX предполагает нулевые разряды порядка у значения в RMR после умножения. Откуда же там взялось 64? Может быть, этот код на ленте испорченный? Всякое бывает.

Если посмотреть по таблице адресов и имён загрузки программы, ошибка происходит в модуле A/SPLUSS. К счастью, у нас есть исходный текст этого модуля в файле besm6.github.io/sources/dubna/besmmons/system1.txt. Вот фрагмент:
  zerl6   :12 , a-x   , =i1-c. up i14.ac=6*a+b+1.a in mag,-8b in sh
, u1a , slicerr grenze negativ
l 12 , a*x , =4002 5252 5252 5253-c. 1/6 exp 64
15 , atx , ganzer teil
, ntr , 7 wegen yta
, yta ,
12 , aox , b48-c rest/6 als gleitkommazahl
l 12 , aax , =7777 7777 7770 0000-c. wegen rundung
12 , a*x , =i-6 -c -b dh neg rest mod 6
Тут всё честно, никаких расхождений. Комментарии на немецком: подтверждение, что Алгол-ГДР действительно делался немцами.

Смотрим реализацию арифметики в эмуляторе. Центральная процедура здесь arith_normalize_and_round(), которая вызывается в завершении каждой арифметической операции и выполняет завершающую нормализацию и округление. В конце видим:
    core.RMR = (core.RMR & ~BITS40) | (mr & BITS40);
То есть мантисса в регистре RMR получает новое значение, а биты порядка остаются старыми. Тут возникло второе подозрение. Зачем нужно держать в регистре мусор от какой-то предыдущей операции? Странно.

А что должно оказываться в битах порядка RMR? Открываем официальную документацию от БЭСМ-6. Смотрим последнюю строчку: "содержимое 41:48 разрядов регистра младших разрядов сохраняется". Чепуха выходит.


Но у нас есть и описание СВС, то есть машины следующего поколения, совместимой по системе команд с БЭСМ-6. Смотрим туда: "содержимое 41-48-го разрядов регистра младших разрядов сохраняется гасится".


Вот он, момент истины! Очевидно, в арифметику СВС было внесено позднее изменение. Возможно, оно было проведено также и в поздних экземплярах БЭСМ-6. И последняя версия дубненского Алгола про это в курсе. А диспаковский алгол, как и весь остальной софт, вероятно был доработан игнорировать биты порядка в РМР, для совместимости с ранними экземплярами БЭСМ-6.

Правим вышеприведённую строчку в эмуляторе: обнуляем биты порядка в РМР. Снова запускаем алгольный пример со строками:
$ dubna algol.dub
...
ABRA CADABRA
R
BRA CAD

Всё работает в лучшем виде. Репутация Алгола-ГДР восстановлена. Мы набрели на необычный момент истории архитектуры БЭСМ-6.
vak: (Default)
Херики-оники, так называлась игра до большевизма. В оригинале это японская гомоку, то есть пять в ряд на доске 15x15.

Предлагаемая вашему вниманию программа для игры в гомоку была написана в 1964 году на языке Алгол-60 для компьютера Electrologica X1. Сообщения и комментарии в программе на голландском. Что логично, так как и компьютер был голландский, и сам Алгол-60 тоже придумали голландцы. Разработка компьютера Electrologica X1 была темой докторской диссертации известного Эдсгера Дейкстры.

Сохранились перфолента и кодом программы и распечатка исходного текста. Материалы можно найти на Гитхабе: github.com/cwi-software-archive/Algol60/tree/main/Quinio.



Лёня [personal profile] spamsink адаптировал эту программу для БЭСМ-6. Вот исходный текст: quinio.dub. Вы можете запустить её на своём компьютере с помощью эмулятора ОС Дубна:
git clone https://github.com/besm6/dubna.git
cd dubna
make install
cd examples/games
dubna quinio.dub
Так выглядит процесс игры:
vak: (Default)
Вот ещё одна симпатичная картинка, нарисованная Графором на БЭСМ-6. Исходники сохранились на ленте ЦЕРНовских тестов, привезённой из Дубны.

vak: (Default)
Самый древний плоттер Calcomp позволял только инкрементные перемещения на один пиксел.



Последующая модель Calcomp-763 уже двигала на два пиксела.

vak: (Default)
Юра Лобачёв много лет не светился в сети, а тут внезапно поминает меня добрым словом. Он отвечал за Си компилятор в новосибирской команде ИТМиВТ, которая переносила Юникс на БЭСМ-6 и Эльбрус-Б. Он взял мою дипломную работу - компилятор Си для БЭСМ-6 и Эльбрус-Б - и довёл до ума.
Новосибирский филиал Института точной механики и вычислительной техники АН СССР

Лобачев Юрий Владимирович

Когда Вы пришли в НФ ИТМ и ВТ?

Заманил меня в НФ ИТМ и ВТ после Университета и после армии в 1981 – Жора Томе, за что ему отдельное спасибо.

Кто был Вашим руководителем?

Сердюк Григорий Иванович.

В каких проектах вы принимали участие?

Сначала было мимолетное участие в КОБОЛе. Потом участвовал, под присмотром Цанга, в проработке системы команд для Эльбруса Б, статистической обработке структур объектных модулей и загрузчиков для формирования этих компонентов для новой системы. Затем была запоминающаяся работа в замечательном творческом коллективе первой лаборатории по установке UNIX-ов на отечественные железяки: OS – Основич, Молчанов; Файловая система – Городилов, Райхерт, Семухина; Отладчики – Горр; мы с Вакуленко портировали первую версию С компилятора; Борис Кузнецов – утилиты и librt. Первой жертвой стала старушка БЭСМ, на которой мы установили мировой рекорд – UNIX на самой старой машине в мире. Затем был UNIX на Эльбрус Б, на него я устанавливал С, Fortran 77 и Pascal. В Фортране реализовал оптимизацию циклов. Последней работой была установка компиляторов С и Fortran 77 на МКП, в Фортран были добавлены векторизующие прагмы, подсмотренные у Крэя.

Потом проснулся – вокруг какой-то Энерджилайн, … Интел, вроде бы все тоже здание, стены, лес, но уже другая страна и совсем другое тысячелетие.

Какой проект, программа или разработка были самыми легкими? Самыми трудными?

Самыми трудными, пожалуй, были гос. сдачи. Порой программа начинала выполнять необходимые требования в последнюю ночь, но провалов не было.

Какой эпизод трудовых будней помнится ярче всего? А какой праздник?

Наверное, длительные командировки с ночным режимом работы. Утром возвращаемся в метро, кроме нас – только ночные бабочки, тоже с работы, примерно в том же состоянии, равнодушно друг друга созерцая. В Москву катались не как пижоны – с дипломатом или сумочкой через плечо, а с большими рюкзаками, чтобы на обратном пути побольше прихватить. Как-то возвращаемся из очередной командировки – два месяца не бритые и не спавшие по ночам, с большими рюкзаками, опаздываем на самолет, подбегаем к такси: “ну как, шеф, успеем?”, в ответ “садитесь мужики, а вы кто – геологи?”

Врезалось в память, как у меня на руках умирал последний отечественный суперкомпьютер. Бартеньев привез фортрановскую задачу для того, чтобы окончательно определиться, будут ли они закупать МКР для Арзамаса. Задача на Эльбрусе Б прошла, а на МКР не желает, потом причина выяснилась – оказалось, так как у нашего суперкомпьютора всего в наличии было 512К слов оперативной памяти, из них половину занимала ОС, и никакой периферии, т.е. массивы было просто некуда положить. И это был конец.

С кем из коллег Вы продолжаете вместе работать? С кем дружите?

Нас тут, на удивление, еще много в этом здании, или вокруг бродят.

Чем Вы занимаетесь сейчас?

А все тем же и занимаюсь – С, Fortran …

В последнее время, правда, стал глубже копать, поэтому периодически попадаются то мамонты, то носороги, то бизоны и большерогие олени. Раньше я не понимал – для чего наши филиальцы в эти бестолковые походы бегают, а теперь для себя сформулировал смысл, и все отпуска провожу на Алтае в компании с единомышленниками. Правда, мы теперь не пешком и с аквалангами.

Где живете? В каких социальных сетях Вас можно найти?

Живу в Академе, часто посещаю палеонтологический форум, там меня можно найти.

vak: (Default)
В середине 80-х в Дубне на БЭСМ-6 появился диковинный девайс: плоттер WX4675 фирмы Watanabe. Народ сбацал выдачу из библиотеки Графор непосредственно на этот плоттер. На симуляторе этот механизм удалось оживить. Берем самый первый пример из книжки по Графору:
*name графор
*call plotter:wx4675,direct
*ftn
program grafor
real x(100), y(100), z(100)
x(1) = -1.9
y(1) = sin(x(1))
z(1) = cos(x(1)) * 1.5
do 5 i=2,90
x(i) = x(i-1) + 0.1
y(i) = sin(x(i))
z(i) = cos(x(i)) * 1.5
5 continue
call page(15., 20., 'PAGE', 4, 1)
call limits(-2., 7., -1.5, 1.5)
call region(3., 3., 10., 15., 'REGION', 6, 1)
call axes('X axis', 6, 1., 5, 'Y axis', 6, 0.3, 4, 0)
call linemo(x, y, 85, 2, 10)
call broken(0.5, 0.2, 0.3, 0.2)
call brline(x, z, 85)
call endpg(0)
end
*execute
*end file
Запускаем, получаем рисунок:



А вот так выглядел сам плоттер.

vak: (Знайка)
Имеем некоторый прогресс в симуляторе мониторной системы "Дубна". Заработали Паскаль, Алгол, Forex, Aspid, Dtran, Whatis. Примеры можно посмотреть по ссылкам:Симулятор умеет теперь выдавать список всех команд мониторки:
$ dubna --help-commands
*           *DISC       *FOREX      *NAME       *PERMA      *TAPE 
*1          *DOS        *FORTR      *NEW L      *PERSO      *TAPES
*ALGOL      *DRUM       *FTN        *NO CH      *PUNCH      *TEMPO
*ASSEM      *EDIT       *FULL       *NO LI      *READ       *TERM 
*ASSIG      *EDIT:      *LIBLI      *NO LO      *READ:      *TIME 
*BEMSH      *END F      *LIBPU      *NO OP      *REFAL      *TO CO
*CALL       *EXCLU      *LIBRA      *NO PU      *SIMUL      *TO PE
*CATAL      *EXECU      *LIST       *OPTIM      *SOS  
*CHECK      *EXFOR      *MADLE      *PAGE       *STAND
*COMMO      *EXPRE      *MAIN       *PASCA      *SYSTE
*COPY       *FILE       *MOVE       *PASS       *TABLE
*COPY:      *FIX D      *MOVE:      *PASS:      *TAKE
А также список библиотек и их расположение на лентах:
$ dubna --help-libs
Library        Tape         Zone
--------------------------------
*library:1     12/librar    0000
*library:2     37/librar    0000
*library:3     37/librar    0340
*library:5     37/librar    0545
*library:6     37/librar    0650
*library:7     37/librar    1061
*library:10    37/librar    1220
*library:11    37/librar    1370
*library:12    12/librar    0375
*library:21    9/monsys     0240
*library:22    9/monsys     0172
*library:23    9/monsys     0320
*library:24    9/monsys     0172
*library:25    12/librar    0640
*library:26    483/grafpr   0000
*library:27    12/librar    0600
Также дошли руки восстановить инструкцию по пакетному редактору *EDIT: besm6.org/wiki/Edit