Оберон-клуб «ВЄДАsoft»
https://zx.oberon.org/forum/

Тонкости сопряжения Оберона, Си, ассемблера
https://zx.oberon.org/forum/viewtopic.php?f=10&t=202
Страница 1 из 3

Автор:  Reobne [ 05 июн 2014, 04:07 ]
Заголовок сообщения:  Тонкости сопряжения Оберона, Си, ассемблера

В этой теме будем обсуждать и развивать тонкие внутренние механизмы, которые помогут писателям библиотек.
Для тех, кто просто хочет писать свои библиотеки, или задаётся вопросом "Как добавить в свою программу код на ассемблере Z80 или на Си?", должно хватить темы: Как создать новую библиотеку для ZXDev

---------------

В Basic.h нашёл:

#define __hash__ #
#define __id__(x) x
#define __ld_a__(x) if(x==0) {__asm xor a,a __endasm;}else{__asm ld a,__id__(__hash__)x __endasm;}
#define __ld_c__(x) __asm ld c,__id__(__hash__)x __endasm

В чём нетавтология первых двух определений?
Оптимально ли сработает третье если х не константа?
(Не проверял, но кажется, что будет построена полная условная конструкция.)

Как называется применяемый диалект ассемблера z80 или где про него можно почитать?
За подсказки что такое # и $ спасибо, помогли. :)

Автор:  Zorko [ 06 июн 2014, 00:04 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Reobne писал(а):
В чём нетавтология первых двух определений?
Код: "C"
#define __hash__ #
#define __id__(x) x
— этот хитрый трюк нужен затем, что в ассемблере sdasz80 перед числовыми литералами и метками нужно обязательно ставить решёточку #. При задании макроса компилятор Си так просто не даёт это сделать. Так что __id__(__hash__) превращается после макропроцессора в #

Такой хитрый выход помог выработать Eltaron, за что ему огромное спасибо.

Reobne писал(а):
Оптимально ли сработает третье если х не константа?
Просто превратит одно в другое. Т.е. LD A,__id__(__hash__)1+2 в LD A,#1+2, а как это транслировать — будет решать sdasz80.

Reobne писал(а):
Как называется применяемый диалект ассемблера z80 или где про него можно почитать?
А вот тут: по ссылочке sourceforge.net/projects/sdcc/files/sdcc-doc/ есть доки, там в архиве раздел sdas посвящён исключительно ассемблеру.

Reobne писал(а):
За подсказки что такое # и $ спасибо, помогли. :)
Там ещё точка обозначает текущий адрес. Т.е.
Код: "ASM"
    DJNZ .    ; MET$: DJNZ MET$
И, в принципе, это почти всё, что следует знать об этом ассемблере. ;)

Автор:  Reobne [ 17 июн 2014, 03:42 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Спасибо, читаю помаленьку. :)

Прочитал(sdccman.pdf 3.14.1.2), что в sdcc ассемблерные вставки можно делать так:
Код: "OBERON"
  1. __asm__ (; This is a comment\nlabel:\n\tnop”);


Тут "\n", это символ перевода строки; "\t" - табуляции.

И подумалось мне, что возможно, было-бы удобно писать ассемблерный текст прямо в обероновском исходнике.
Код: "OBERON"
  1. MODULE AsmTest;
  2. IMPORT Asm;
  3.  
  4. BEGIN (*$MAIN*)
  5. Asm.Code(' PUSH HL');
  6. Asm.Code(' LD HL,#0h4000');
  7. Asm.Code(' LD (HL),#1');
  8. Asm.Code(' INC H');
  9. Asm.Code(' LD (HL),#3');
  10. Asm.Code(' INC H');
  11. Asm.Code(' LD (HL),#7');
  12. Asm.Code(' INC H');
  13. Asm.Code(' LD (HL),#6');
  14. Asm.Code(' INC H');
  15. Asm.Code(' LD (HL),#255');
  16. Asm.Code(' INC H');
  17. Asm.Code(' LD (HL),#127');
  18. Asm.Code(' INC H');
  19. Asm.Code(' LD (HL),#127');
  20. Asm.Code(' INC H');
  21. Asm.Code(' LD (HL),#255');
  22. Asm.Code(' POP HL');
  23. END AsmTest.


Решил попробовать. Написал интерфейс библиотеки Asm
Код: "OBERON"
  1. MODULE Asm;
  2. (*PLATFORM Spectrum48;*)
  3.  
  4. PROCEDURE Code*(IN str: ARRAY OF CHAR); BEGIN END Code;
  5. END Asm.


Сохранил в "ZXDev\Lib\Mod"
Компильнул "F11"
Скопировал получившиеся Asm.c и Asm.h из "ZXDev\Lib\Obj" в "ZXDev\Lib"
Переправил хедер
Код: "OBERON"
  1. /* Ofront 1.2 -xtspkae */
  2.  
  3. #ifndef Asm__h
  4. #define Asm__h
  5.  
  6. #include "SYSTEM.h"
  7.  
  8.  
  9. #define Asm_Code(a,b) __asm__ ("; This is a comment\n\tnop")
  10.  
  11. import void *Asm__init(void);
  12.  
  13.  
  14. #endif
  15.  


Из Asm.c выкусил Asm_Code, (так как оно теперь в хедере, как предпроцессорный "макрос").
Собрал библиотеку.
Попробовал компилить тестовый пример - не получается :(
Потыкался, оказалось в батнике ZXDev\Bin\build.bat надо прописать "Asm.lib"

В таком виде всё компилится и собирается, но "; This is a comment\n\tnop" - это заглушка. Надо подставлять строку с кодом из аргумента.
И вот тут возник затык.
Тестовый пример даёт такой сишный код
Код: "OBERON"
  1. export main(int argc, char **argv)
  2. {
  3. __INIT(argc, argv);
  4. __IMPORT(Asm__init);
  5. __REGMAIN("AsmTest", 0);
  6. /* BEGIN */
  7. Asm_Code((void*)&" PUSH HL", (LONGINT)9);
  8. Asm_Code((void*)&" LD HL,#0h4000", (LONGINT)15);
  9. Asm_Code((void*)&" LD (HL),#1", (LONGINT)12);
  10. Asm_Code((void*)&" INC H", (LONGINT)7);
  11. Asm_Code((void*)&" LD (HL),#3", (LONGINT)12);
  12. Asm_Code((void*)&" INC H", (LONGINT)7);
  13. Asm_Code((void*)&" LD (HL),#7", (LONGINT)12);
  14. Asm_Code((void*)&" INC H", (LONGINT)7);
  15. Asm_Code((void*)&" LD (HL),#6", (LONGINT)12);
  16. Asm_Code((void*)&" INC H", (LONGINT)7);
  17. Asm_Code((void*)&" LD (HL),#255", (LONGINT)14);
  18. Asm_Code((void*)&" INC H", (LONGINT)7);
  19. Asm_Code((void*)&" LD (HL),#127", (LONGINT)14);
  20. Asm_Code((void*)&" INC H", (LONGINT)7);
  21. Asm_Code((void*)&" LD (HL),#127", (LONGINT)14);
  22. Asm_Code((void*)&" INC H", (LONGINT)7);
  23. Asm_Code((void*)&" LD (HL),#255", (LONGINT)14);
  24. Asm_Code((void*)&" POP HL", (LONGINT)8);
  25. __FINI;
  26. }
  27.  


Думаю, что из-за этого привидения типа "(void*)&", которое ОФронт понавставлял, и не работает.
Кажется, осталось немного и всё заработает. Либо обмануть ОФронт, чтобы не генерировал "(void*)&", либо как-то хитро сишным предпроцессором его откусить. Мне пока не удалось.

Автор:  Zorko [ 17 июн 2014, 14:02 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

У меня тоже возникала эта проблема, и я не смог её решить. Ofront всегда приводит строку если не к "(void*)&" (в случае IN-параметра), то к "(CHAR*)". То есть требуется переделка Ofront'а. Пока не знаю как это лучше сделать. По поводу макропроцессора нужно консультироваться с продвинутыми сишниками. Но мне кажется, и здесь мало чего светит.

Предлагаю пока так:
Код: "OBERON"
  1. MODULE AsmTest;
  2. IMPORT _ := Asm;
  3.  
  4. BEGIN (*$MAIN*)
  5. _.Byte2( 3EH, 1); (* LD A, #1 *)
  6. _.Byte2(0D3H,0FEH); (* OUT (#0xFE), A *)
  7. END AsmTest.


Reobne писал(а):
Да, так можно.
Ещё можно каждой ассемблерной инструкции сделать обёртку и писать, например:
Код: "OBERON"
  1. Asm.LD_A_PEEK_HL;
  2. Asm.LD_A_CONST(12+3);
  3. Asm.LD_A_INT((I+5) MOD 16);
  4. Asm.LD_BYTEVAR_A(Char1);
  5. Asm.LD_WORDVAR_HL(X);


Автор:  Saferoll [ 19 июн 2014, 18:52 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Reobne писал(а):

Думаю, что из-за этого привидения типа "(void*)&", которое ОФронт понавставлял, и не работает.
Кажется, осталось немного и всё заработает. Либо обмануть ОФронт, чтобы не генерировал "(void*)&", либо как-то хитро сишным предпроцессором его откусить. Мне пока не удалось.
Возможно поможет средство Ofront PROCEDURE -
Цитата:
For procedures, Ofront allows a "-" sign after the keyword PROCEDURE in a procedure declaration to indicate that this procedure is an in-lined C code sequence.
The in-lined code is written in quotation marks after the procedure heading as in the following example:
Код: "OBERON"
  1. PROCEDURE -malloc(size: LONGINT): LONGINT
  2. "((LONGINT)malloc(size))";
Ofront translates such procedures into macro definitions which are subject to C preprocessing.
Код: "OBERON"
  1. #define Mymodule_malloc(size) ((LONGINT)malloc(size))
Obviously, this mechanism provides a way to interface Oberon with foreign languages such as C or assembly language, as explained in more detail in Section 4.2.

Автор:  Reobne [ 20 июн 2014, 02:36 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Saferoll писал(а):
Возможно поможет средство Ofront PROCEDURE -

Большое спасибо! Эта возможность ОFront-а удобна для написания библиотек. OFront сам генерирует #define. Править Си и Н файлы теперь можно меньше, или даже совсем не править! Одна проблема решена!!

Осталось, для "полного счастья" найти, как описать ему тип аргумента строку, чтобы она и оставалась строкой, без приведения типа.

Автор:  Zorko [ 20 июн 2014, 12:13 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Saferoll писал(а):
Возможно поможет средство Ofront PROCEDURE -
Попробовал и так, не получается:
Код: "OBERON"
  1. PROCEDURE -Code* (cmd: ARRAY OF CHAR) "__asm__(cmd)";
  2. ...
  3. Asm.Code("ld a, #4");
  4. Asm.Code("out (#254),a");
  5.  
  6. (* всё равно превращается в: *)
  7.  
  8. Asm_Code((CHAR*)"ld a, #4", (LONGINT)9);
  9. Asm_Code((CHAR*)"out (#254),a", (LONGINT)13);
Проблему несомненно нужно как-то решать, а то вот даже Basic.DATA* удалось сделать для символов, байтов, слов и двойных слов, а со строками не получается. Раз подняли — будем решать. :)

Думаю попробовать модифицировать Ofront, чтобы помечать строковые параметры, которые не нужно приводить ни к какому типу, системным тегом [1], т.е. вот так:
Код: "OBERON"
  1. PROCEDURE -Code* (cmd: ARRAY[1] OF CHAR) "__asm__(cmd)";
Сейчас такие параметры приводятся к типу CHAR*, и раньше я активно использовал их чтобы избежать дублирования строк, но после добавления в Ofront IN-параметров использовать IN для этой цели даже ещё лучше, чем тег [1], потому что не нужен импорт SYSTEM. Так что вроде бы неплохое решение.

Автор:  Zorko [ 20 июн 2014, 13:46 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Сделал. В процедуру OPV.ActualPar добавляем:
Код: "OBERON"
  1. IF ~(n^.typ^.comp IN {Array, DynArr}) THEN
  2. IF n^.typ^.sysflag # 0 THEN (* Ok *)
  3. ELSIF mode = VarPar THEN
После чего нужно пересобрать весь Ofront: Docu/Rebuild-Ofront.odc

Определение Code на Обероне:
Код: "OBERON"
  1. PROCEDURE -Code* (cmd: ARRAY[1] OF CHAR) "__asm__(cmd)";
Asm.Code("xor a") оттранслировалось в:
Код: "OBERON"
  1. ;AsmTest.c:21: Asm_Code("xor a", (LONGINT)6);
  2. xor a

Автор:  Reobne [ 20 июн 2014, 13:49 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Кажется получилось! :)
Код: "OBERON"
  1. #define Asm___KILLER__(a)
  2. #define Asm_Code(str, str__len) __asm__(Asm___KILLER__ str)
  3.  


KILLER берёт как свой аргумент (CHAR*) и ликвидирует его.

Автор:  Zorko [ 20 июн 2014, 16:43 ]
Заголовок сообщения:  Re: Тонкости сопряжения Оберона, Си, ассемблера

Браво, Reobne! Элегантно и оригинально! :) У вас определённо нестандартное мышление. Подпихнуть ненужное приведение типа как фиктивный параметр отдельного макроса — это надо додуматься. :)

На базе этого решения придумывается такая идея (думаю, легко будет реализовать):
Код: "OBERON"
  1. Asm.Code4(
  2. " LD A, #1 ",
  3. " OUT (#0xFE), A ",
  4. " LD B, #123 ",
  5. " METKA$: DJNZ METKA$ "
  6. );
Так ещё чище асм-код будет смотреться. А с учётом того, что сейчас Ofront умеет сам добавлять "линии разреза", — и смартлинковкой будет пользоваться проще. Правда, наверное ещё будут проблемы с переменными, будем дорабатывать по мере сил. :) Так что поздравляю, мы уже можем начинать разработку библиотек для Z80, не выходя напрямую на уровень Си.

Ну и откажемся от доработки тега [1], как я предлагал выше, — чтобы не разбивать совместимость. А то я уже засомневался и хотел делать для такого случая тег [2].

Страница 1 из 3 Часовой пояс: UTC + 2 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/