Oleg N. Cher, как говорится, RTFM. htc_doc.zip
Oleg N. Cher, как говорится, RTFM. htc_doc.zip
С уважением,
Gris / Red Triangle.
_____________________________________
ZX-EVO/TS-Labs config/NGS/HDD/SD-card
Amiga A1200/Blizzard 1230@50/32/60GB
Amiga A1200/Apollo 1260@66/32/60GB
UnAmiga (C5) AGA GM7123 VideoDAC
Итак о моих мытарствах. Наконец-то получил более или менее приемлемый результат. Мой Hello.c
Код:void main (void) { # asm ld a,'H' rst 16 ld a,'E' rst 16 ld a,'L' rst 16 ld a,'L' rst 16 ld a,'O' rst 16 # endasm }Попытка добавить ключик -O при компиляции приводит к нерабочему сломанному коду. Это конечно фича.Код:@SET MainMod=Hello @SET CodeAdr=25000 @CD CPM @..\cpm -h c.com -C %MainMod%.c @IF errorlevel 1 PAUSE @..\cpm -h link.com -Z -Ptext=%CodeAdr% -C%CodeAdr% %MainMod%.OBJ libc.lib @IF errorlevel 1 PAUSE @CD .. @stripbin.exe CPM\L.bin @bin2tap.exe -c 24999 -a %CodeAdr% -r %CodeAdr% -b -o %MainMod%.tap CPM\L.bin @PAUSE
Ключик -O"Hello.bin" для линкера не работает, довольствуюсь именем по умолчанию - L.bin
Нагенерил это. Выглядит не очень, какие-то непонятные пляски с бубном. Ничего подобного в SDCC я не встречал (издержки CP/M?):
Вопрос к Sergey и Sayman: насколько безопасно отрезать ВСЕ коды 1A в конце бинаря? (не выйдет ли, что последний байт с этим кодом будет нужным?)
P.S. Можно я плюну на лысину авторам подобных высказываний?Умиляшка просто. Ох уж эти теоретики, мля...
- - - Добавлено - - -
Hi-Tech C берёт код входа в программу и выхода в ОС из библиотеки libc.lib, наверное как-то можно заменить эти подпрограммы на пустые. Но возня. Если libc.lib не подать параметром на вход линкера, получаем:
Код от SDCC. Для сравненияHI-TECH C COMPILER (CP/M-80) V3.09
Copyright (C) 1984-87 HI-TECH SOFTWARE
undefined symbols:
cret
ncsv
indir
Ни одного лишнего байта.Код:_main:: ld a,#'H' rst 16 ld a,#'E' rst 16 ld a,#'L' rst 16 ld a,#'L' rst 16 ld a,#'O' rst 16 ret
Ладно, это конечно не совсем честное сравнение, до Си мы так и не дошли.
- - - Добавлено - - -
Нашёл исходники библиотек Hi-Tech C v3.09, прикладываю.
libsrc.zip
Вот и код для cret, ncsv и indir. Оказывается, он аллокирует количество байт на стеке, указанное в переменной, которая живёт у нас по адресу 25003 (после CALL на нашу main).
А вот, например, зацените ABS:Код:global csv,cret,indir, ncsv psect text csv: pop hl ;return address push iy push ix ld ix,0 add ix,sp ;new frame pointer jp (hl) cret: ld sp,ix pop ix pop iy ret indir: jp (hl) ; New csv: allocates space for stack based on word following ; call ncsv ncsv: pop hl push iy push ix ld ix,0 add ix,sp ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl add hl,sp ld sp,hl ex de,hl jp (hl)
Даже сходу хочется поменять "ld hl,0 : or a" на "xor a : ld l,a : ld h,a". Впрочем, мы с вами выяснили, что правильный ABS для слов выглядит так. Для Спектрума нужно переписать библиотеки заново. Есть желающие окунуться в дружественный мир хайтех Си? ;-)Код:; abs(i) returns the absolute value of i global _abs psect text _abs: pop de ;Return address pop hl push hl push de bit 7,h ;Negative? ret z ;no, leave alone ex de,hl ld hl,0 or a ;Clear carry sbc hl,de ret
Installing the HI-TECH Z80 C Compiler for CP/M
Странное знакомство. По идее вызова функции BDOS хватает.
- - - Добавлено - - -
а флаги не потеряются, не?
Да и пусть себе теряются, компилятор не ждёт от функции сохранности флагов (и правильно делает).
Шынни, мне CP/M не интересна как таргет, вместо этого хочу приспособить компилятор для разработки под Спектрум, а тут BDOS нет.
- - - Добавлено - - -
Продолжим исследования. Хайтех для входа в любоую функцию генерирует:
Спишем на то, что хайтек - компилятор однопроходный. И не знает на момент генерации кода входа в функцию, сколько байтов надо выделить на стеке для лок. переменных. Это он узнаёт уже потом.Код:CALL ncsv DW 0 ; Сколько байт резервировать на стеке для локальных переменных FnBody: ... ; Тут начинается сама функция ; А заканчивается она не RET, а JP cret
SDCC же многопроходный, поэтому вместо вызова фрейма входа и выделения 0 байтов на стеке он просто вызывает функцию напрямую. И она возвращается по RET.
Я не готов сходу дать список отличий Hi-Tech C v3.09 и SDCC, но в этом SDCC сильно лучше. Вызов функций - вещь базовая, и она должна быть устроена максимально производительно. Возражения?
SDCC с ключиком --opt-code-size генерит такой вход в процедуру:
C ключиком --opt-code-speed:Код:call ___sdcc_enter_ix push af push af ; Надо выделить 4 байта на стеке ... ld sp, ix pop ix retКод:push ix ld ix,#0 add ix,sp push af push af ; Надо выделить 4 байта на стеке ... ld sp, ix pop ix ret
Последний раз редактировалось Oleg N. Cher; 19.01.2017 в 13:13.
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Версию для ms-dos есть желания попробовать, руки не дошли. Но вроде как она сильно лучше, где-то в теме "Ищу Си для Z80" проскакивала ссылка на результаты сравнения качества кода для разных Z80-компилеров, не могу найти.
Reobne, ни один известный мне компилятор Си не воспользуется для оптимизации тем фактом, что регистр A не портится внутри функции. Кроме SDCC - если указать __preserves_regs(a). Но это философский вопрос, что лучше - меньше тактов или не портить A.
- - - Добавлено - - -
Не, вру. z88dk тоже имеет модификатор __preserves_regs, он оттуда в SDCC и пришёл. Но неясно качество такой оптимизации...
просто забавно, что из Си вываливается.
сравните atol для z88 и HiTech
HiTech CP/M 3.09 (from zip posted by Oleg)
Написанное в C и использует общий 32-битного умножения для каждой итерации.Код:#include <ctype.h> long atol(s) register char * s; { long a; unsigned char sign; while(*s == ' ' || *s == '\t') s++; a = 0; sign = 0; if(*s == '-') { sign++; s++; } while(isdigit(*s)) a = a*10L + (*s++ - '0'); if(sign) return -a; return a; }
Скрытый текст
Written in C and uses a general 32-bit multiply for each iteration.
[свернуть]
Z88DK 1.99B (new c library)
Написано в ассемблере и использует сдвиги для умножения в каждой итерации. Эта версия также проверяет наличие переполнения или сгущенного и заглавные буквы результат соответствующего макс или мин значений.Код:; =============================================================== ; Dec 2013 ; =============================================================== ; ; long atol(const char *buf) ; ; Read the initial portion of the string as decimal long and ; return value read. Any initial whitespace is skipped. ; ; =============================================================== SECTION code_clib SECTION code_stdlib PUBLIC asm_atol EXTERN l_eat_ws, l_eat_sign, l_neg_dehl, l_atoul asm_atol: ; enter : hl = char *buf ; ; exit : bc = char *buf (next unprocessed char, could be digit on overflow) ; dehl = long result ; carry set on overflow (dehl clamped to LONG_MAX or LONG_MIN) ; ; uses : af, bc, de, hl call l_eat_ws ; skip over any initial whitespace call l_eat_sign ; consume any leading sign jr nc, not_negative ; if there was no minus sign ; negative sign found call not_negative ; convert numerical part jp nc, l_neg_dehl ; if no overflow, negate result inc de inc hl ; dehl = LONG_MIN = $80000000 ret not_negative: ex de,hl call l_atoul ; unsigned long conversion jr c, overflow ; unsigned overflow bit 7,d ; check for signed overflow ret z scf ; indicate signed overflow overflow: ld de,$7fff ld h,e ld l,e ; dehl = LONG_MAX = $7fffffff ret ..... ..... SECTION code_clib SECTION code_l PUBLIC l_small_atoul l_small_atoul: ; ascii buffer to unsigned long conversion ; whitespace is not skipped ; char consumption stops on overflow ; ; enter : de = char * ; ; exit : bc = & next char to interpret in buffer ; dehl = unsigned result (0 on invalid input) ; carry set on unsigned overflow ; ; uses : af, bc, de, hl ld c,e ld b,d ld de,0 ld l,e ld h,d dec bc push de push hl loop: pop af pop af inc bc ld a,(bc) sub '0' ccf ret nc cp 10 ret nc push de push hl add hl,hl rl e rl d jr c, overflow_0 push de push hl add hl,hl rl e rl d jr c, overflow_0 add hl,hl rl e rl d jr c, overflow_0 ex de,hl ex (sp),hl add hl,de pop de ex (sp),hl adc hl,de ex de,hl pop hl jr c, overflow_1 add a,l ld l,a jr nc, loop inc h jr nz, loop inc e jr nz, loop inc d jr nz, loop overflow_1: pop hl pop de scf ret overflow_0: pop af pop af jr overflow_1
Реализация z88dk, вероятно, немного больше, но это считалось хорошим компромиссом для таких функций, как это, где скорость может оказать влияние на производительность больших программ. Существует также быстрый вариант библиотеки z88dk, который использует "l_fast_atoul" вместо "l_small_atoul". Разница является быстрая версия пытается вычислить результат в 16 бит, а затем перемещается только до 32 бит, когда число становится большим.
Я ожидаю версию z88dk быть намного быстрее; если вас интересует, и вы можете дать мне двоичный (сырой z80, а не ф / м зависит) и Тест С программы я могу время два для сравнения. Существует утилита "ticks" в z88dk, который может измерять Z80 циклы точно.
Скрытый текст
Written in asm and uses shifts for multiplication in each iteration. This version also checks for overflow or underflow and caps the result to respective max or min values.
The z88dk implementation is probably a little bit bigger but that was thought a good compromise for functions like this where speed can have an impact on the performance of large programs. There is also a fast variant of the z88dk library that uses "l_fast_atoul" instead of "l_small_atoul". The difference is the fast version tries to compute the result in 16 bits first and then only moves to 32 bits when the number becomes large.
I expect the z88dk version to be a lot faster; if you are interested and you can give me a binary (raw z80, not cp/m dependent) and a test c program I can time the two for comparison. There is a utility "ticks" in z88dk that can measure z80 cycles exactly.
[свернуть]
Последний раз редактировалось Alcoholics Anonymous; 19.01.2017 в 20:37.
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)