Просмотр полной версии : GCC 11.2.1 с поддержкой процессоров 1801вм1 и 1801вм2
После продолжительных и многократных подходов в GCC была добавлена поддержка процессоров 1801вм1 и 1801вм2.
Бинарники, собранные под Windows с помощью MING-W64, выложены Славе на сайт:
http://www.1801bm1.com/files/pdp11/cross-compilers/ming-w64-pdp11-aout-11.2.1.zip
c4d55afc5150262f9ebbe3a9749a9d86 ming-w64-pdp11-aout-11.2.1.zip
Дата сборки: 13.11.21 ~ 16:41
Выбор модели "target" осуществляется опциями `-mbm1a`, `-mbm1g` или `-mbm2` в дополнение к оригинальным `-m10`, `-m40` и `-m45`.
`-mbm1` есть алиасом для `-mbm1a` -- процессор 1801вм1а поддержки MUL, как в 1801вм1г.
Нужно заметить, что данная сборка - это чистый кросс-компилятор без стандартной библиотеки C, без libgcc (нет софтверной реализации MUL, DIV операций). Дело наживное, можно "потянуть" из наработок DEC.
Соотвественно, буду со временем допиливать libgcc и базовую libc для RT11 (работа с файлами для начала).
Сборка поддерживает только компиляторы C и C++. Попытка добавить Ada и Fortran с наскока не получилось, возможно оно требует установки каких-то компонентов в систему при сборке, или какие-то специальные ключи конфигураци GCC.
Известно, что для свежего GCC 11+ есть Modula-2 frontend. Возможно, попробую прикрутить его для любителей изделий Вирта (Паскаль).
В сборку так же входят утилиты bin2load.exe и lda2sav.exe для облегчения получения выполняемых файлов в RT11.
Есть репозиторий с примером использования кросс-компилятора для генерации файлов в формате Files11 (*.SAV), использование ASM вставок для вывода символов/строк с помощью EMT 0341, 0351
https://github.com/yshestakov/pdp11-toolchain/tree/master/hello-gcc
Oleg N. Cher
10.11.2021, 04:28
Поделитесь правильной последовательностью создания .sav-файлов при помощи утилиты lda2sav?
Я делаю так:
@ECHO OFF
:: http://ancientbits.blogspot.com/2012/07/programming-barebones-pdp11.html
SET CodeAdr=01000
SET Options=-Wno-write-strings -fverbose-asm -quiet -O2 -Os -fomit-frame-pointer
SET Include=-I ..\Lib\C -I ..\Lib\Obj -I ..\Lib
SET Libraries=%Libraries% -L ..\Lib ..\Lib\CGLIB.lib
SET PATH=..\Bin\gcc\bin
SET CC=..\Bin\gcc\libexec\gcc\pdp11-aout\10.0.1\cc1.exe
SET AS=pdp11-aout-as.exe
%CC% %Options% %Include% %1.c
IF errorlevel 1 PAUSE
%AS% %1.s -o %1.o
IF errorlevel 1 PAUSE
pdp11-aout-ld.exe --entry %CodeAdr% -T ldaout.cmd %1.o -o %1.out %Libraries%
IF errorlevel 1 PAUSE
bin2load.exe -a -f %1.out -o %1.lda -b %CodeAdr%
IF errorlevel 1 PAUSE
lda2sav.exe -o %1.sav %1.lda
IF errorlevel 1 PAUSE
Здесь всё делается при помощи скрипта ldaout.cmd, задающего структуру .sav-файла, как я понимаю. Сам ldaout.cmd взят с этой ссылки (http://ancientbits.blogspot.com/2012/07/programming-barebones-pdp11.html). Но я всё-таки в глубине души подозреваю, что можно как-то без него... тем более, что полученный мной .sav с точки зрения IDA какой-то подозрительный. Хотя реальщики проверили на живом УКНЦ, работает. Но какое-то смутное сомнение всё-таки осталось...
- - - Добавлено - - -
P.S. Проверил насчёт выпиливания SETD/SETI. Не выпиливаются. Пока так.
http://i.piccy_.info/i9/19404e356613a251c0f14d69ab94c2b5/1636507618/70497/1320134/SETD.png
Последовательность команд сборки SAV включая crt0.s, конфиг линкера LD (pdp11-aout.ld):
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/Makefile
По поводу вставки setd/seti в начало функции main() -- нашел, что оно зависит от "TARGET_FPU" (CPU поддерживает аппаратные операции с float point numbers). У меня там еще есть косяки вокруг парсера опций, исправлю.
Отключается SETI/SETD добавлением опции `-msoft-float`:
pdp11-aout-gcc -mbm2 -msoft-float -c -o hello.o hello.c
Oleg N. Cher
10.11.2021, 18:44
А что это за метка ___main и откуда она берётся? Компилирую пустой модуль, где в конце просто emt 0350 для выхода в RT-11.
pdp11-aout-ld.exe: Empty.o:Empty.o:(.text+0x2): undefined reference to `__main'
http://i.piccy_.info/i9/48b69edd6ea01063e720dea7b9e24855/1636557695/44356/1320134/GCC.png
- - - Добавлено - - -
Последовательность команд сборки SAV включая crt0.s, конфиг линкера LD (pdp11-aout.ld):
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/Makefile
Это точно рабочий мейкфайл? А то там опции вызова GCC странные: -m10 вместо -mbm2
pdp11-aout-ld.exe: cannot open linker script file pdp11-aout.ld: No such file or directory
Так ругается на строку:
pdp11-aout-ld -T pdp11-aout.ld -o $@ $(OBJS) -Map=hello.out.map
Должен быть ещё скрипт pdp11-aout.ld, в этом и был вопрос.
И что - работает без задания адреса?
1. Функция ___main, возможно, есть частью стандартной libgcc или что-то вообще из новых стандартов C. В любом случае в crt0.s файле можно обьявить метку с инструкций "rts pc"
2. Да, Makefile рабочий. Просто я этот проект отлаживал года полтора-два тому, когда еще не сделал поддержки -mbm1/-mbm2. Опция -m10 -- это PDP11/10 с процессором LSI-11, подходит для ВМ1. Да, легко поменять под свои нужды
3. Файл pdp11-aout.ld лежит рядом с Makefile в репозитории. Сравните со своим LD скриптом. У меня там стартовый адрес 01000
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/pdp11-aout.ld
Поборол обработку опций для процессоров BM1/BM2:
* выключен FPU для этих процессоров
* генерация инструкций `seti / setd` в начале функции main() отключена
Из особенностей: для BM1 не разрешена MUL инструкция, потому `-mbm1` означает процессор 1801BM1a, а не BM1г. Исправлю в следующей сборке: добавлю `-mbm1g` или `-mbm1a` с соответствующей обработкой в генераторе инструкций
Oleg N. Cher
11.11.2021, 18:25
1. Функция ___main, возможно, есть частью стандартной libgccНо ведь GCC должен сам подхватывать libgcc без доп. параметров типа -lgcc?
В любом случае в crt0.s файле можно обьявить метку с инструкций "rts pc"Даже не знаю, где находится этот crt0.s
Сравните со своим LD скриптом.Отличаются одной строкой (в Вашем есть, в моём нет):
crt0lda.o(.text)
Кстати, откуда всё-таки берутся эти crt0*? Они не в libgcc вшиты?
Но ведь GCC должен сам подхватывать libgcc без доп. параметров типа -lgcc?
Даже не знаю, где находится этот crt0.s
Отличаются одной строкой (в Вашем есть, в моём нет):
crt0lda.o(.text)
Кстати, откуда всё-таки берутся эти crt0*? Они не в libgcc вшиты?
Нет, crt0*.s файлы никогда не были частью libgcc. Это "переходник" между средой (RT-11 как OS, или LDA, как ROM) и функцией main(). Функция main() на вход принимает argc и argv (аргументы программы).
Кстати, в далеком 88 году, когда мне в руки попал компилятор C для RT-11 на ДВК/УКНЦ, в комплекте не было crt0.s файла. Пришлось разбираться и писать его самому.
В случае "демо" проекта на GCC, есть 2 вариант crt0 файла: для RT-11 и для LDA (ROM):
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/crt0rt.s
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/crt0lda.s
В том, что для RT11, используется EMT для корректного выхода обратно в OS. Там же нужно сделать обработку командной строки.
Oleg N. Cher
11.11.2021, 20:33
Спасибо.
Правильно ли я понимаю, что в RT-11 нет возможности передать в программу командную строку?
И Вы её эмулируете путём передачи argc=1 argv[]="hello.rom" ?
Может проще выпилить? Я вот так делаю:
#define main(argc, argv) main(void)
Обязательно ли задавать своё значение SP=01000? Прога не может работать с тем SP, что ей достался на момент её вызова?
В RT-11 есть возможность разобрать аргументы, переданные в программу. Просто я это не умею (забыл как делается).
На предмет SP=01000 - это такая договоренность. Программа (TEXT сегмент) начинается с 01000, а стек растет вниз от 01000.
SuperMax
12.11.2021, 06:26
Спасибо.
Правильно ли я понимаю, что в RT-11 нет возможности передать в программу командную строку?
И Вы её эмулируете путём передачи argc=1 argv[]="hello.rom" ?
Может проще выпилить? Я вот так делаю:
нее
если CИ делать то разбор командной строки очень нужен!
касаемо как это сделано в RT11 можно прочитать тут
https://forum.maxiol.com/index.php?showtopic=5004
ПО ДВК [TIFF] Книга 6 - Системные библиотеки и сообщения системы (Научный центр-1990)
запрос .CSISPC страница 54ая
shattered
12.11.2021, 19:24
или поискать исходники c runtime от decus c
Да, нашел runtime decus. Посмотрел бегло. Пока прикручу функции EIS для BM1 для начала. Это функции, которые должны быть в libgcc, по идее. Далее можно, собственно, портировать базу libc: IO (rt11), выделение памяти, printf, strings и т.д.
Цель: получить минимальный, но рабочий комплект для написания программ на C/C++ под RT-11 используя кросс-компилятор GCC.
Oleg N. Cher
13.11.2021, 06:13
Ну, мне сишные библиотеки нужны постольку поскольку, хотя юзать их из Оберона тоже можно.
Традиционно в библиотеке XDev есть модуль CmdLine. Если он будет реализован для RT-11, то вовсе не обязательно тем же способом, что сишный механизм argc/argv.
Я смотрю, много чего на асме для RT-11 делается через макросы. Так вот. Из GCC у нас нет возможности подключить файл этих макросов, взятый, например, из MACRO-11 - придётся всё делать на уровне асма, вызовами EMT и прочим, предусмотренным в RT-11. Пока я не понял как развернуть запрос .CSISPC в команды GNU ассемблера. Но понял, что .EXIT разворачивается в emt 0350 и т.д.
BlaireCas
13.11.2021, 09:25
как развернуть запрос .CSISPC в команды GNU ассемблера. Но понял, что .EXIT разворачивается в emt 0350
В самом macro-11 можно глянуть как это делается использовав директиву .list MEB. И тогда в листинге он "развернет" макросы. Например .csispc будет:
1 .mcall .csispc
2 .list MEB
3 000000 START: .csispc
000000 005046 CLR -(SP)
000002 005046 CLR -(SP)
000004 005046 CLR -(SP)
000006 104345 EMT ^o<345>
4 000000' .end START
.MAIN. MACRO V05.06R Saturday 13-Nov-21 08:34 Page 1-1
А чтобы вытащить самому системные макросы из SYSMAC.SML - можно "расковырять" код портированного на винду macro-11. (вот этого http://retrocmp.com/tools/macro-11-on-windows). Он тоже умеет развернуть макросы в листинг, правда более много "не лишнего" в листинг выводит.
Если макрос простой - то можно туповатую утилиту препроцессора написать которая regexp-ами позаменяет всякие .ttyin например. Предварительно набив библиотеку этих макросов запуская настоящий macro-11 на временных файлах. Системных макровызовов не слишком много ведь.
- - - Добавлено - - -
Бинарники, собранные под Windows с помощью MING-W64, выложены Славе на сайт
Заметил такую штуку что в сборке недостает msys-2.0.dll (тот-же lda2sav без нее не запустить). Оно естественно быстро нагугливается где скачать отдельно данную .dll, но все-же может стоит ее добавить? (не у всех будет mingw наверняка).
Вообще-то, с командной строкой у RT-11 все плохо. Она принимает только командные строки формата CSI, то есть список выходных файлов от одного до трех, далее, знак "=" и список до шести входных файлов. При каждом из файлов могут быть один или несколько однобуквенных ключиков типа /A, /B=177 (oct), /C=88. (dec), /D=ABC (rad50). И все.
Начиная с RT-11 v05.00 допускается записать эти списки в несколько измененном виде в одну строку с вызовом программы, а именно,
RU DEV:PROG <список входных файлов> <список выходных файлов>, через пробел (внутри списков пробелы не допускаются). Да, для вызова программы с SY: RU DEV: набирать не надо, достаточно имени программы.
Начиная, кажется, с 05.03 или с 05.04 в RT-11, наконец-то, добавили передачу остатка командной строки в неизменном виде через поле памяти, начиная с адреса 510 (вроде бы, точно не помню), но рассчитывать, что RT-11 будет 5.4 или новее нельзя - есть, допустим, такая интересная система, как АДОС (зеленоградская), которая была сделана из 5.1 и ничего про это не знает...
Так вот, KMON системы RT-11 берет этот остаток командной строки и преобразует его к формату CSI, то есть переставляет в начало список выходных файлов, затем ставит знак равенства и список входных файлов, пробел, естественно, выбрасывает. То есть, если мы наберем
PROG IFIL1,IFIL2 OFIL1,OFIL2
то программа PROG будет вызвана с SY: и получит команду CSI
OFIL1,OFIL2=IFIL1,IFIL2
Типы каждого из выходных файлов, а также тип всех входных файлов указывается в запросе .CSIGEN или .CSISPC и, если не указать типы файлов явно (например, IFIL1.DAT,IFIL2.NUM), то типы файлов будут взяты из запроса CSIGEN/CSISPC.
Все Си PDP-11, которые я пробовал, запрашивают командную строку отдельным запросом GTLIN и разбирают ее по пробелам (выделяют отдельные слова из строки), что получается из остатка командной строки после преобразования ее в формат CSI, увы, не помню.
- - - Добавлено - - -
Заметил такую штуку что в сборке недостает msys-2.0.dll (тот-же lda2sav без нее не запустить). Оно естественно быстро нагугливается где скачать отдельно данную .dll, но все-же может стоит ее добавить? (не у всех будет mingw наверняка).
Спасибо!
Сам по себе GCC (его компоненты) собраны как "static" и не требуют msys-2.0.dll.
А вот lda2sav.exe надо поправить, пересобрать из исходников.
Пересобрал lda2sav.exe и bin2load.exe, проверьте пожалуйста свежий архив
/usr/bin/i686-w64-mingw32-gcc-9.3-win32 -o lda2sav.exe -static lda2sav.c
Возможно, нужно использовать i686-w64-mingw32-gcc-9.3-posix вместо i686-w64-mingw32-gcc-9.3-win32
В обоих случая бинарник получается с зависимосью только на KERNEL32.dll и msvcrt.dll
Нашел винду, проверил: bin2load и lda2sav.exe запускаются норм без msys-2.0dll
BlaireCas
13.11.2021, 23:04
Удалось скомпилять простую программку, работает вроде, печатает что надо.
Заглянув дебагером эмулятора обнаружил DIV и использование остатка деления, все верно скомпиляло.
extern int rt11_print(int i, const char* ptr);
int inline rt11_print(int i, const char* ptr)
{
asm ("mov 4(sp), r0");
asm ("add $48, r0");
asm ("emt 0341");
asm ("bcs .-2");
asm ("mov 6(sp), r0");
asm ("emt 0351");
}
const char* txt_msg1 = " - Hello, world!";
void main()
{
for (int i=20; i<30; i++)
{
int j = i%10;
rt11_print(j, txt_msg1);
}
} Но однако! Пока допетрил как все собрать ... жжуть.
Хорошо в стартовом посте ссылка на hello где есть pdp11-aout.ld и crt0lda.s из которого надо crt0lda.o ассемблировать.
Кстати упростил его, пашет
;
.text
.GLOBAL _main
.GLOBAL start
start:
mov $01000, sp
jsr pc, _main
emt 0350
.GLOBAL ___main
___main:
rts pc
.end Иначе вот ни в жизнь бы не догадался как скомпилить хоть что-нибудь :)
Вдруг кто тоже будет долго допетривать как под виндой собрать хеллоуворлд, а make берет вот под виндой и.. не пашет пушто его нету :)
1) путь на \bin в PATH
2) взять c гитхаба в стартовом посте файлы: pdp11-aout.ld, crt0lda.s, crt0lda.o (можно и упрощенный crt0lda.s выше привел)
2) если модифицировали crt0lda.s, то его скомпилять надо pdp11-aout-as crt0lda.s -o crt0lda.o (один раз достаточно сделать)
3) батником ниже компиляется тестовый файл: build.bat test
set PATH=.\bin
pdp11-aout-gcc -mbm2 -msoft-float -c -o %1.o %1.c
pdp11-aout-ld --entry 01000 -T pdp11-aout.ld %1.o -o %1.out
bin2load -a -f %1.out -o %1.lda -b 01000
del /f %1.sav
lda2sav.exe -o %1.sav %1.lda
Вообщем хорошо что есть рабочий GCC. (а то я пробовал DECUS C для УКНЦ - вот там адъ и погибель, там настоящий Керниган и Ричи стайл, про анси не слышали, только самый трушный С, только хардкор)
Oleg N. Cher
14.11.2021, 10:32
Тогда раз обращение к параметрам идёт через 4(sp) и 6(sp), то значит атрибут inline игнорится?
int inline rt11_print(int i, const char* ptr)
{
asm ("mov 4(sp), r0");
...
}
Кстати, если добавить опцию -fomit-frame-pointer, то указатель фрейма не будет сохраняться на стеке и секции enter/leave будут покомпактнее, а 4(sp) превратится в 2(sp).
BlaireCas
14.11.2021, 13:21
если добавить опцию -fomit-frame-pointer, то указатель фрейма не будет сохраняться на стеке
О, да. Так стало намного лучше.
А насчет inline - я вообще сомневаюсь что в GCC это работает без включения опций оптимизации. Но с включенной оптимизацией навроде -О1 - скомпиленное "ломается" и у меня не заработало в итоге. "Тонкие" настройки не пробовал. Можно как вариант атрибут на функцию попробовать повесить что-то типа always_inline.. Не изучал такой момент.
Насчет регистров - самому казалось что тут будет как для ARM, то-есть первые четыре параметра вне зависимости от опций компилятора всегда идут через R0-R3. Но нет, идет через стек. Даже если по стечению обстоятельств один из параметров идет через R0 - в стек он все-равно будет засунут.
у PDP-11 слишком мало регистров, чтоб через них передавать параметры. Например, в том же i386 так же все через стек сделано. Только в AMD64 изменили ABI, плюс регистров добавили.
Поломать GCC, чтоб передавать параметры через регистры -- можно. Вот только в внутри вызванной функции опять таки будет использоваться стек, чтоб сохранить R0...R4 на стек и уже относительно стека работать со значениями
BlaireCas
14.11.2021, 15:34
Поломать GCC, чтоб передавать параметры через регистры -- можно
Хакир :) Но возможно это не требуется. Ассемблерные процедурки которые требуют скорости - они вообще без параметров-же будут (ожидая значения в регистрах). А регистры можно загрузить через asm() как в примере там показано:
#define RT11_TTYOUT(c) asm(" mov %[input_c],r0\n emt 0341\n bcs .-2\n" : : [input_c] "r" (c) : "r0", "cc" )
Я не знаю что означают символы : : например, надо гуглить синтаксис. Но оно работает и судя по дизасму все верно делает. Вставляет код который загружает параметр в регистр R0 и вызывает EMT и надеюсь компилятор понимает что регистр R0 был использован и учитывает это.
- - - Добавлено - - -
Чего еще хочется от GCC - чтобы FIS инструкции (эти четыре FADD, FSUB, FDIV, FMUL) использовал и формат float-а был-бы DEC-овский. Например на УКНЦ эти инструкции повешены пусть и на прерывание, но у меня работали в какой-то версии RT-11.
А то ценность сишника без плавучки нереально сильно падает.
Я посмотрю, что можно сделать с поддержкой FIS для 1801ВМ2.
Уже пора прикрутить тесты собранного кода на "железной" реализации 1801ВМ1 и 1801ВМ2 в виде FPGA.
То есть разбираться с DejaGNU framework, написать минимальный ROM, который будет уметь только LDA грузить и работать с 1 COM-портом, сделать соотвествующее описание "board" в DejaGNU и методы работы с ним.
BlaireCas
14.11.2021, 16:40
Попробовал на УКНЦ комманды FIS, например FADD сработало. Вообще даже не встраивая их в сам GCC можно прикрутить обертки как я понимаю для операций с float. Но я не знаю какой формат GCCшного float-a да и конечно хорошо если бы они встроены были.
Как пример теста - складывает 12.0 и 12.0 получается 24.0, работает норм. Команды типа fadd ассемблер понимает, уже плюс.
#define _TTYOUT(c) asm(" mov %[input_c],r0\n emt 0341\n bcs .-2\n" : : [input_c] "r" (c) : "r0", "cc" )
#define LOAD_R0(c) asm(" mov %[input_c],r0\n" : : [input_c] "r" (c) : "r0", "cc" )
/*
char* buf = "00000";
extern void rt11_decimal (unsigned int u)
{
unsigned int k = 5;
while (u > 0) { buf[--k] = u%10+48; u = u/10; }
while (k < 5) RT11_TTYOUT(buf[k++]);
}
*/
extern void rt11_bin (unsigned int u)
{
for (int k=0; k<16; k++) {
if (u&0x8000) _TTYOUT('1'); else _TTYOUT('0');
u = u<<1;
}
}
unsigned int fbuf[4];
void main()
{
// 12.0f
unsigned int Ahi = 0b0100001001000000; // [1-bit sign] [8-bits exponent, 177=-1, 200=0, 201=1 etc] [24-bit mantissa with first hidden bit always assume = 1]
unsigned int Alo = 0b0000000000000000;
// 12.0f
unsigned int Bhi = 0b0100001001000000;
unsigned int Blo = 0b0000000000000000;
// output
rt11_bin(Ahi); rt11_bin(Alo); _TTYOUT('+'); _TTYOUT('\n'); _TTYOUT('\r');
rt11_bin(Bhi); rt11_bin(Blo); _TTYOUT('='); _TTYOUT('\n'); _TTYOUT('\r');
// struct for fis
fbuf[0] = Bhi;
fbuf[1] = Blo;
fbuf[2] = Ahi;
fbuf[3] = Alo;
// fadd
LOAD_R0(fbuf);
asm ("fadd r0\n");
// output
rt11_bin(fbuf[2]); rt11_bin(fbuf[3]);
}
в DECUS есть реализации FADD, FSUB, FMUL. Это можно "притянуть" в GAS (GNU Asm)/GCC. Но вот отмаппить `float` на FIS -- не просто, кмк
Oleg N. Cher
15.11.2021, 01:33
Директива asm в GCC имеет дополнительные параметры для макроподстановки входных и выходных аргументов. Думаю, мы должны их использовать вместо явного указания N(sp) и это, возможно, поможет правильно генерировать код в inline-функциях:
void Palette (int n)
{
asm("BICB $0177774, %0"::"g"(n)); // превращается в BICB $0177774, 02(sp)
...
}
https://ru.wikipedia.org/wiki/GCC_Inline_Assemblyhttp://asmcourse.cs.msu.ru/wp-content/uploads/2013/04/gcc-inline-asm.pdf
Oleg N. Cher
26.11.2021, 11:57
Коллеги, как можно установить вручную адрес секции .data? У меня .data всегда устанавливается в адрес (.code + 0x2000)
Хотя по содержимому скрипта pdp11-aout.ld этого и не скажешь.
То есть, когда юзаются библиотеки с переменными, я получаю бинарник от 8 Кб.
Простые неинициализированные переменные в .data ложатся раньше константного массива.
Если выкусить секцию .data вообще:
pdp11-aout-objcopy.exe --only-section .text --output-target binary %1.out ..\%1.bin
то мы получаем нормальный маленький бинарь, но константные массивы тоже выкусываются.
В идеале хотелось бы, чтобы константный массив ложился в секцию .data раньше, чем неинициализированные переменные (чтобы не увеличивать ими размер бинаря).
Пробовал пихать код и данные в одну секцию .text:
OUTPUT_FORMAT("a.out-pdp11")
ENTRY(start)
phys = 00001000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata);
data = .;
*(.data)
}
end = .;
}
Но с таким скриптом сборки переменные налазят на константный массив и занимают с ним одно адресное пространство. Помогает ALIGN:
OUTPUT_FORMAT("a.out-pdp11")
ENTRY(start)
phys = 00001000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(0100);
data = .;
*(.data)
}
end = .;
}
Но при таком решении приходится регулировать размер в ALIGN ручками, чтобы константный массив не налазил на переменные.
В общем, нужен хороший совет. Как сделать, чтобы секция .data ложилась сразу после секции кода?
Попробовал на УКНЦ комманды FIS, например FADD сработало. Вообще даже не встраивая их в сам GCC можно прикрутить обертки как я понимаю для операций с float. Но я не знаю какой формат GCCшного float-a да и конечно хорошо если бы они встроены были.
IEEE-754 single precision, разумеется. Который совместим с плавучкой от PDP-11, только слова в обратном порядке.
Полезно посмотреть на *.map файл, там будет раскладка символов по адресам.
ALIGN можно сделать маленьким, хоть 010.
Не инициализированные переменные ложатся в секцию BSS.
Перезалил свои примеры `hello-gcc` на github.
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/pdp11-aout.ld
Oleg N. Cher
26.11.2021, 16:28
Насчёт ALIGN'а понятно, спасибо. Непонятно почему наслаиваются друг на друга переменные и конст. массив. И почему задано такое огромное окошко между .text и .data (0x2000). И как задать его размер. Это ведь не ALIGN 0100 так работает. Даже если убрать ALIGN - будет то же самое.
Среди опций pdp11-aout-objcopy.exe вроде бы нужных нет (чтобы задать размер окна между .text и .data).
Да, один нюанс: я экспериментирую с генерацией не в .sav, а в .bin для БК-0010/0011. Возможно, дело в этом.
Попробуйте генерить .bin и использовать переменные из других модулей и библиотек. Также я могу залить свой пример, чтобы помочь воспроизвести проблему (только вот кто будет его ковырять?)
- - - Добавлено - - -
Не инициализированные переменные ложатся в секцию BSS.Вы в этом точно уверены? Мне так не показалось. По-моему, GCC для PDP-11 вообще не использует секцию .bss
за 0x2000 - не скажу, не понимаю, о чем речь.
Вот свежий вариант LD скрипт для SAV файла:
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/pdp11-sav.ld
В нем в 1й блок файла (512байт) сделан в виде savhdr.o файла. То есть структура заголовка SAV описана в savhdr.s.
В частности, это позволяет в адрес 050 (Program’s high limit) внести адрес метки `.end` -- где конец программы с данными
- - - Добавлено - - -
- - - Добавлено - - -
Вы в этом точно уверены? Мне так не показалось. По-моему, GCC для PDP-11 вообще не использует секцию .bss
Нет, уже не уверен. Смотрю в файл hello.out.map, вижу секция .bss есть, но не инициализированная переменная, или инициализированная 0-лем попадает в секцию data.
Надо разбираться. Я за год+ все забыл.
Судя по всему, `pdp11-sav.ld` -- не рабочий. Я пользую pdp11-aout.ld скрипт + lda2sav программу для преобразования LDA->SAV.
- - - Добавлено - - -
При `ALIGN(040)` не работает rt11_print() и puts() функции. При `ALIGN(0100)` -- все работает. Почему так? Не помню. Надо разбираться, опять таки.
размер всей программы hello минус 512 байт заголовка == 384 байта. При этом на ALIGN между секциями фактически ушло 100 байт. Много ли это или мало?
Oleg N. Cher
27.11.2021, 17:00
100 байт это не так много конечно, но наверное стандарты RT-11 этого требуют. Других объяснений почему могут не работать функции rt11_print() и puts() - у меня нет.
Итак, я беру Ваш pdp11-sav.ld и сырой бинарь после pdp11-aout-ld.exe (в скрипте же уже выбран формат OUTPUT_FORMAT("binary") вместо "a.out-pdp11" как было раньше). Попутно появилась мысль не вызывать pdp11-aout-objcopy.exe для конвертации a.out в bin вообще. Но не тут-то было. Бинарь pdp11-aout-ld.exe даёт, но настраивает его с нашим скриптом крайне криво - при заданном стартовом адресе 01000 (для БК-0010/0011М) адрес константного массива находится _ниже_ адреса кода. Вот сейчас отладчик показывает адрес константного массива 0424, т.е. константный массив размещён _после_ кода, а адрес настроен как будто бы он находится _до_ кода. Притом увеличение этого числа ничего не даёт:
.data : AT(phys + data + 1000)
Я вынужден опять откатиться до формата a.out и вызова objcopy - так хотя бы криво, но работало. А бинарь после aout-ld со съехавшими адресами массивов вообще нерабочий. Повторюсь, что в случае вызова lda2sav этой проблемы наверное нет.
Кстати, может быть, что константные массивы ложатся в секцию (или подсекцию? как это правильно?) .rodata, а не .bss?
- - - Добавлено - - -
Я воспроизвёл проблему на маленьком исходнике:
76511
Как видим, здесь адрес массива равен 016, а должен быть больше 01000.
Я склоняюсь к тому, что проблема есть и в .sav, просто Вы не дошли до работы с конст. массивами.
http://i.piccy_.info/i9/10db24ac18ae7f7c1ee35b2eee4bca33/1638021372/27632/1320134/Test.png
- - - Добавлено - - -
Кстати, здесь и адрес данных получился странный, наложился на последнюю команду в коде. Видите? Хотя ALIGN(010) в скрипте есть.
Да, с LD скриптом проблема: при малом ALIGN (<0100) у меня портится строка, которая выводится. Починю - маякну
Oleg N. Cher
28.11.2021, 00:16
Спасибо.
Смотрите как я генерю рабочий бинарь с правильными смещениями (это может помочь. Наверное):
OUTPUT_FORMAT("a.out-pdp11")
ENTRY(start)
phys = 00001000;
SECTIONS
{
.text phys : AT(phys)
{
code = .;
*(.text)
*(.rodata)
. = ALIGN(phys);
*(.data);
}
}
.bss убираю вообще, а .data подпихиваю в .text
Теперь так:
pdp11-aout-ld.exe -T %PdpDev%\Bin\pdp11-bin.ld %PdpDev%\Lib\crt0.o %1.o %Modules% %Libraries% -o %1.out
IF errorlevel 1 PAUSE
pdp11-aout-objcopy.exe --output-target binary %1.out ..\%1.bin
IF errorlevel 1 PAUSE
%PdpDev%\Bin\make-bk.exe ..\%1 512
IF errorlevel 1 PAUSE
Здесь всё хорошо, кроме ". = ALIGN(phys)" перед данными. Что туда добавить - так и не понял. Делаю ALIGN(phys+phys) и тоже работает, хотя ничего визуально не меняется (даже сам бинарь). Делаю мелкое число - глючит.
Коллеги, напоминаю. Традиционным для Си на PDp-11 считается расположение переменных и массивов, локальных для любой С-функции, включая main(), в стеке. Из-за чего обычных для RT-11 96 слов стека (192 байта) между тоже традиционным для RT-11 началом программы 001000 и "желтой" границей стека 000500, чаще всего, не хватает. Поэтому программы из-под компиляторов Си, обычно, линкуют с ключиком /BOT команды LINK, указывая конкретный адрес, откуда начнется программа и ниже которого будет стек.
Я при написании и отладке программ на С, обычно, сначала указывал /BOT:5000, а далее отладка покажет, много это или мало...
желтой" границей стека 000500
Жёлтая граница стека - ниже 400, красная - ниже 340
Ну и если есть регистр границы стека - возможны варианты - он задаёт значение старшего байта для границы стека, а в младшем - см выше.
Значит так.
Утилита `bin2load.exe` (a.out -> LDA) имеет внутри harcoded значение ALIGN секции = 0100.
Если в LD скрипте выставить значение ALIGN меньше 0100, то смешение в результирующем LDA не будут соответствовать тому, что прописано в A.OUT файле линкером.
#define SECT_ALIGN 0100
/*
** If we have a data section, we align it to a boundary and
** save it in a new block.
** In this version the boundary is fixed (SECT_ALIGN, defined in bin2load.h)
** I'll give the chance of specifying it as a program argument... perhaps
**
** WARNING: The boundary size MUST be the same as the specified in the link
** script, else BAD THINGS WILL HAPPEN...
*/
Oleg N. Cher
30.11.2021, 01:14
Но дело не только в bin2load, ведь я её не использую. Или где-то ещё захардкожено 0100?
Такой вопрос. Столкнулся с тем, что при умножении идёт вызов к неопределённой подпрограмме __mulhi3:
;# Test.c:51: Laser2_PTBL2(x + 1, y * 10 + 28, 103);
mov $012,-(sp) ;#,
mov 026(sp),-(sp) ;# y,
jsr pc,___mulhi3 ;#
add $04,sp ;#,
Это, получается, 16-битное умножение. Хотя не очень понятно что здесь обозначает hi и 3.
Где-то есть готовая эффективная реализация умножения для 1801BM1 ?
Функция __mulhi3 должна быть частью LIBGCC, но для PDP11 она не реализована. И вообще, libgcc для PDP-11 я ее не собирал (не дошли руки):
https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/pdp11/t-pdp11
hi -- half-integer (16-bit) в терминах GCC. qi -- quarter integer (8 bit), si -- 32-bit integer.
https://www.cse.iitb.ac.in/grc/intdocs/gcc-implementation-details.html
в Decus-C я нашел реализацию умножения над 2-мя long. Думаю, ее и надо брать за основу для реализации __mulhi3 (для 1801ВМ1 в общем).
http://www.ibiblio.org/pub/academic/computer-science/history/pdp-11/language/decus-C/5,4/mull.mac
Там рядом лежит fmul.mac, кстати.
Для процессора MSP430 эта функция реализована так:
/* Public domain. */
extern int __mulhi3 (int, int);
int
__mulhi3 (int x, int y)
{
char bit;
int neg = 0;
int rv = 0;
if (y < 0)
{
y = - y;
neg = 1;
}
for (bit = 0; y && bit < sizeof (y) * 8; bit ++)
{
if (y & 1)
rv += x;
x <<= 1;
y >>= 1;
}
return neg ? - rv : rv;
}
Oleg N. Cher
30.11.2021, 12:50
В журнале "Персональный компьютер БК-0010, БК-0011М" №2-1995 есть такая реализация умножения:
; Подпрограмма умножения
; 8-разрядных чисел
; R2-множимое, R3-множитель,
; R4-результат, R5-счетчик итераций
MUL: MOV #10, R5 ; Цикл из 8 итераций
CLR R4 ; Очистить аккумулятор результата
1: ASR R3 ; Очередной разряд множителя
ВСС 2 ; Если 0 - дальше
ADD R2, R4 ; Иначе прибавить множимое
2: ASL R2 ; Сдвиг множимого на один разряд
SOB R5, 1 ; Если не конец - в цикл
RET ; Выход
; Алгоритм этой программы построен совершенно аналогично
; правилу умножения «в столбик» для двоичных чисел
; и, видимо, в пояснениях не нуждается.
Вроде выглядит неплохо. Меня смутило только то, что заявлено умножение только 8-разрядных чисел. Как можно её модифицировать для 16-разрядных? Также я могу расковырять подпрограмму умножения из OMSI Pascal. Надо?
- - - Добавлено - - -
А хотя, скорее всего, OMSI Pascal юзает аппаратное умножение, он же для 1801BM2 и RT-11.
Заменить #10 на #20. Но тогда, естественно, уже возможно перепеолнение.
Код совершенно классический, есть в любом букваре. Есть также классическая вариация с выходом по обнулению второго множителя. Она в среднем быстрее, если множитель маленький, то в разы.
; r0 = r1 * r2
mul: clr r0
cmp r1, r2
bhis 1$
xor r1, r2
xor r2, r1
xor r1, r2
1$: tst r2
beq 0$
clc
ror r2
bcc 2$
add r1, r0
2$: asl r1
add r1, r0
asr r2
bcs 2$
beq 3$
sub r1, r0
br 2$
3$: sub r1, r0
0$: rts r7
Набил по памяти, но вроде правильно. Это т.н. алгоритм с восстановлением, классическая оптимизация. Тело цикла умножения -- это 2$..bcs 2$ для единичного бита и 2$..br 2$ для нулевого. Выполняется до исчерпания единичных бит в меньшем множителе.
Можно ещё быстрее, но это приводит к ещё большему объёму кода.
А ещё быстрее -- через таблицу квадратов, пользуясь следующим равенством:
((x + y) ^ 2) / 4 - ((x - y) ^ 2) / 4 = x * y;
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot