Сообщения: 1019 Откуда: Днепропетровская обл.
|
Вдохновлённый модулем Asm, предложенным Reobne, решил проверить эффективность создания Оберон-бибиотеки без выхода на уровень Си. Вот что получилось. Оригинальный модуль на Modula-2:Код: "OBERON" DEFINITION MODULE GRAPH0; CONST width=256;height=192; PROCEDURE SCRDOT; PROCEDURE SCRNAD; PROCEDURE CLS2; PROCEDURE GCLS; PROCEDURE SETATTRS(X,Y:CARDINAL;L,C:SHORTCARD); END GRAPH0. IMPLEMENTATION MODULE GRAPH0; PROCEDURE CLS2;HEX 3A 8D 5C 06 18 D9 21 00 5B CD (@GCLS+20) END CLS2; PROCEDURE GCLS;HEX AF 06 BF D9 21 00 58 CD (@GCLS+20) 21 00 40 06 20 77 2C 10 FC C9 D9 57 5F 21 00 00 39 D9 F9 D9 D5 D5 D5 D5 D5 D5 D5 D5 D5 D5 D5 D5 D5 D5 D5 D5 10 EE F9 C9 END GCLS; PROCEDURE SETATTRS(X,Y:CARDINAL;L,C:SHORTCARD);HEX DD E1 C1 E1 D1 7B E6 1F 5F 7D 0F 0F 0F 57 E6 E0 B3 6F 7A E6 03 F6 58 67 04 18 02 71 23 10 FC C5 C5 C5 DD E9 END SETATTRS; PROCEDURE SCRDOT;HEX 7A E6 07 C6 18 6F 26 BD 7A 56 END SCRDOT; PROCEDURE SCRNAD; HEX 0F 0F 0F E6 1F 6F 7B 07 07 E6 E0 B5 6F 7B E6 07 47 7B 0F 37 1F 37 1F E6 58 B0 67 C9 END SCRNAD; END GRAPH0.
Портированный вариант: Код: "OBERON" MODULE GRAPH0; (* For the ZX Spectrum (c) Mira Software 1991 *) (** This module provides the elementary procedures used by theother graphics modules. CLS2 clears the screen, but not the attributes area, while GCLS clears the screen and attributes. SETATTRS(X,Y,L,C) sets L character squares to have attribute C starting at character position (X,Y). SCRDOT and SCRNAD are imported by other graphic modules, and are not useable as procedures on their own. *) IMPORT S := SYSTEM, Asm; CONST width* = 256; height* = 192; PROCEDURE CLS2* ; (** clears the screen, but not the attributes area. *) BEGIN Asm.Code("LD A, (#0x5C8D) ;ATTR_P "); Asm.Code("LD B, #0x18 "); Asm.Code("EXX "); Asm.Code("LD HL, #0x5B00 ;SWAP_PAGE"); Asm.Code("JP _GRAPH0_GCLS+20 "); END CLS2; PROCEDURE GCLS* ; (** clears the screen and attributes. *) BEGIN Asm.Code("XOR A "); Asm.Code("LD B, #0xBF "); Asm.Code("EXX "); Asm.Code("LD HL, #0x5800 "); Asm.Code("CALL _GRAPH0_GCLS+20 "); Asm.Code("LD HL, #0x4000 "); Asm.Code("LD B, #0x20 "); Asm.Code("LD (HL), A "); Asm.Code("INC L "); Asm.Code("DJNZ .-2 "); Asm.Code("RET "); (*_GRAPH0_GCLS+20:*) Asm.Code("EXX "); Asm.Code("LD D, A "); Asm.Code("LD E, A "); Asm.Code("LD HL, #0x0000 "); Asm.Code("ADD HL, SP "); Asm.Code("EXX "); Asm.Code("LD SP, HL "); Asm.Code("EXX "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("PUSH DE "); Asm.Code("DJNZ .-16 "); Asm.Code("LD SP, HL "); (*Safe RETURN to BASIC:*) Asm.Code("LD HL, #0x2758 "); Asm.Code("EXX "); END GCLS; PROCEDURE SETATTRS* (X, Y: S.CARDINAL; L, C: S.SHORTCARD); (** sets L character squares to have attribute C starting at character position (X, Y). *) BEGIN Asm.Code("POP BC "); Asm.Code("POP DE "); Asm.Code("POP HL "); Asm.Code("LD A, E "); Asm.Code("AND A, #0x1F "); Asm.Code("LD E, A "); Asm.Code("LD A, L "); Asm.Code("RRCA "); Asm.Code("RRCA "); Asm.Code("RRCA "); Asm.Code("LD D, A "); Asm.Code("AND A, #0xE0 "); Asm.Code("OR E "); Asm.Code("LD L,A "); Asm.Code("LD A,D "); Asm.Code("AND A, #0x03 "); Asm.Code("OR A, #0x58 "); Asm.Code("LD H, A "); Asm.Code("POP DE "); Asm.Code("PUSH BC "); Asm.Code("PUSH BC "); Asm.Code("PUSH BC "); Asm.Code("PUSH BC "); Asm.Code("LD B, E "); Asm.Code("INC B "); Asm.Code("JR B07E$ "); Asm.Code("B07C$:"); Asm.Code("LD (HL), D "); Asm.Code("INC HL "); Asm.Code("B07E$:"); Asm.Code("DJNZ B07C$ "); END SETATTRS; PROCEDURE SCRDOT* ; (** imported by other graphic modules. *) BEGIN Asm.Code("LD A, D "); Asm.Code("AND A, #7 "); Asm.Code("ADD A, #0x18 "); Asm.Code("LD L, A "); Asm.Code("LD H, #0xBD "); Asm.Code("LD A, D "); Asm.Code("LD D, (HL) "); END SCRDOT; PROCEDURE SCRNAD* ; (** imported by other graphic modules. *) BEGIN Asm.Code("RRCA "); Asm.Code("RRCA "); Asm.Code("RRCA "); Asm.Code("AND A, #0x1F "); Asm.Code("LD L, A "); Asm.Code("LD A, E "); Asm.Code("RLCA "); Asm.Code("RLCA "); Asm.Code("AND A, #0xE0 "); Asm.Code("OR L "); Asm.Code("LD L, A "); Asm.Code("LD A, E "); Asm.Code("AND A, #7 "); Asm.Code("LD B, A "); Asm.Code("LD A, E "); Asm.Code("RRCA "); Asm.Code("SCF "); Asm.Code("RRA "); Asm.Code("SCF "); Asm.Code("RRA "); Asm.Code("AND A, #0x58 "); Asm.Code("OR B "); Asm.Code("LD H, A "); END SCRNAD; PROCEDURE- _init* "/*noinit*/"; END GRAPH0.
Скрипт Obj/GRAPH0.bat выглядит так: Код: "WINBATCH" @SET Lib=z80\MiraMod2.lib @IF EXIST ..\%Lib% DEL ..\%Lib% @CALL ..\Bin\smart %Lib% GRAPH0 -noinit - Устанавливаем путь и имя библиотеки, в которую будет добавлен наш модуль.
- Если библиотека уже существует, удалим старый файл во избежание накладывания новых сущностей поверх старых. У меня были с этим проблемы, пэотому рекомендую всегда так делать.
- Вызов скрипта smart, которому передаются путь-имя библиотеки, название модуля и ключик, который отказывается от добавления в библиотеку инициализатора модуля. Соответственно модуль библиотеки описывает процедуру PROCEDURE- _init* "/*noinit*/", которая транслируется Ofront'ом в макрос-пустышку #define GRAPH0__init() /*noinit*/. Это для эффективности, чтобы не включать в библиотеку лишний код.
Теперь опишем скрипт Bin/smart: Код: "WINBATCH" @REM args: @REM LibName ModName [PartName] [-noinit] [-nocut] @CD .. @SET RootBin=..\..\Bin @COPY Obj\%2.h %RootBin%\smartlib Obj\%2.c %3 %4 %5 @FOR %%i IN (%2_0??.c) DO ( ..\Bin\sdcc -c %%i -mz80 --opt-code-size --disable-warning 59 --disable-warning 85 -I "." -I include -I Obj @IF errorlevel 1 PAUSE ) @FOR %%i IN (%2_0??.rel) DO ..\Bin\sdar -rc %1 %%i @CALL Bin\clear @CD Obj @CD .. - выйти из папки Lib/Obj на уровень выше — в Lib. @SET RootBin=..\..\Bin - задаётся путь к корневой папке Bin (в ней находится утилита smartlib). @COPY Obj\%2.h - копируется заголовок Obj\Модуль.h в Модуль.h. Это наша традиционная папка, в которой ищутся библиотечные хидеры (а почему они не ищутся в Lib/Obj? Да просто потому, что хидеры могут быть написаны вручную и отличаться от автоматически сгенеренных Ofront'ом). %RootBin%\smartlib Obj\%2.c %3 %4 %5 - вызов smartlib с параметрами, полученными из вызывающего скрипта. Это могут быть имя части и ключи -noinit и -nocut ( для использования этих ключиков пришлось доработать утилиту smartlib). Далее описан цикл, который вызывает последовательно компиляцию каждого фрагмента исходника, разрезанного smartlib. Если в результате компиляции фрагмента будет найдена ошибка, компиляция будет остановлена командой @IF errorlevel 1 PAUSE. Далее тоже в цикле осуществляется упаковывание оттранслированных объектных файлов в библиотеку с помощью утилиты sdar. @CALL Bin\clear - чистит рабочую папку от временных файлов, нарезанных фрагментов и объектников. @CD Obj - входим в папку Obj. Именно в этой папке мы были перед вызовом скрипта. Пара комментариев напоследок. - Оверхед в CLS2. Процедура завершается командой:
JP _GRAPH0_GCLS+20
после которой SDCC конечно же генерирует ret. Оверхед размером в 1 байт, не снижающий скорость работы процедуры, ибо управление до него не доходит. Решается выходом на уровень Си и использованием тега __naked.
- Вызов части другой процедуры по смещению. Успешно работает, в т.ч. и с "умной" линковкой. Т.е. процедура CLS2 зависима от GCLS, поскольку вызывает её. В случае использования только GCLS процедура CLS2 не включается в целевой бинарник. Если же использовать CLS2, то включаются обе.
- Я, помнится, утверждал, что для Оберон-процедуры всегда генерируется фрейм входа и выхода. Оказалось, что это неверно. Мне не удалось установить закономерность в этом вопросе, да я особенно и не рылся, но действительно сейчас для процедуры void GRAPH0_SETATTRS (CARDINAL X, CARDINAL Y, SHORTCARD L, SHORTCARD C) фреймы входа и выхода не генерируются. Что не может нас не радовать. Кстати, в этой процедуре интересно устроен способ получения из стека параметров, я такого ранее не использовал, но теперь буду иметь его ввиду.
Так что попытка разработать библиотеку без выхода на уровень Си увенчалась вполне успешно. Имеем только один камешек в огород — оверхед в 1 байт, но, большей частью, это не так страшно, ведь решение всё равно есть. Можно даже разработать системный тег, который будет генерировать __naked, только вот эти вот все пляски с бубном для экономии одного байта выглядят слишком уж наивными, я предпочитаю сконцентрироваться на других, более важных направлениях. Ну и маленькая демонстрационная программа, использующая нашу библиотеку.
|
|