Oleg N. Cher, как говорится, RTFM. Вложение 59414
Вид для печати
Oleg N. Cher, как говорится, RTFM. Вложение 59414
Итак о моих мытарствах. Наконец-то получил более или менее приемлемый результат. Мой 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?):
http://i.piccy_.info/i9/47d35c13d8eb...4/HelloAsm.png
Вопрос к Sergey и Sayman: насколько безопасно отрезать ВСЕ коды 1A в конце бинаря? (не выйдет ли, что последний байт с этим кодом будет нужным?)
http://i.piccy_.info/i9/cacaf63eb9da...1065564/1A.png
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, прикладываю.
Вложение 59454
Вот и код для 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
Версию для 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.
[свернуть]