LD DE, 0000 + ADD HL, DE = 10+4 = 14 тактов
LD A,H + OR L = 5+4 = 9 тактов
Но если DE загружать вне цикла, то покатит.
Вид для печати
LD DE, 0000 + ADD HL, DE = 10+4 = 14 тактов
LD A,H + OR L = 5+4 = 9 тактов
Но если DE загружать вне цикла, то покатит.
LXI 10 тактов. DAD тоже. Итого 20.
---------- Post added at 17:37 ---------- Previous post was at 17:36 ----------
Давай будем разговаривать в мнемониках i8080 :)
http://www.emuverse.ru/wiki/Intel_80...B0%D0%BD%D0%B4
DAD = 4 такта
---------- Post added at 15:39 ---------- Previous post was at 15:38 ----------
А в Intel_8080_ASM_Lang_Manual.pdf написано 10
Я тут просматривал код:
И подумал - а не стоит ли переменные засовывать прямо в код, для циклов? Выделенную строчку заменить на две:Код:; for(x=0; x<64; x+=16) {
xor a
ld (main_x), a
l10:
ld d, 64
ld a, (main_x)
call l_uchar
or a
jp z, l11
jp l12
l13:
ld a, (main_x)
add 16
ld (main_x), a
jp l10
l12:
; }
jp l13
l11:
main_x_var:
ld a,0
нолик, соответственно, место где хранится переменная, она же теперь часть кода. Соответственно, обращаться к переменной теперь не ld a, (main_x), а ld a, (main_x_var+1). Код на байт короче, байт экономится на глобальной переменной, 6 тактов на каждой итерации цикла. Правда 2 минуса - нужно следить за обращением к x, он теперь на новом месте, и такой код нельзя зашить в ROM.
Хм, сделаю и такой режим :) Но когда переменные лежат одним блоком, легко реализовать рекурсию. Просто этот блок переменных копировать в стек.
Выхожу на финишную прямую с кодогенератором. Сделал операции < > <= >= == != для uchar. Сделал #define #undef (как полагается с параметрами).
В этом коде еще убрать дублирующиеся присваивания переменных (ld a, 1 ld (print_x), a ld a, 1) или (ld (atoi_a), hl, ld hl, (atoi_a)) и т.д. Эта уборка будет отключатся словом volatile. Объединить XOR A и LD (HL), A, например (xor a ld hl, (clrscr_dest) ld (hl), a). И убрать лишние переходы, например jp $+3 или jp на другой jp.Код:print_return ds 0
print_x ds 1
print_y ds 1
print_text ds 2
print_dest ds 2
print:
push bc
ld (print_text), hl
; 10 dest = (uchar*)(0xE1D5 + x + y*78);
ld hl, (print_x)
ld h, 0
ld de, 57813
add hl, de
push hl
ld d, 78
ld a, (print_y)
call mul_uchar
pop de
add hl, de
ld bc, hl
; 11 while(*text) { *dest = *text; dest++; text++; }
l2:
ld hl, (print_text)
ld a, (hl)
or a
jp z, l3
; 11 *dest = *text; dest++; text++; }
ld hl, (print_text)
ld a, (hl)
ld (bc), a
; 11 dest++; text++; }
inc bc
; 11 text++; }
ld hl, (print_text)
inc hl
ld (print_text), hl
jp l2
l3:
l1:
pop bc
ret
;----------------------------------
clrscr_return ds 0
clrscr_dest ds 2
clrscr_c ds 2
clrscr:
push bc
; 17 dest = (uchar*)0xE1D0-1;
ld hl, 57807
ld (clrscr_dest), hl
; 18 c = 78*25;
ld bc, 1950
; 19 do *++dest = 0; while(c--);
l5:
; 19 *++dest = 0; while(c--);
ld hl, (clrscr_dest)
inc hl
ld (clrscr_dest), hl
xor a
ld hl, (clrscr_dest)
ld (hl), a
dec bc
ld a, b
or c
jp nz, l5
l4:
pop bc
ret
;----------------------------------
get:
; 23 return 23;
ld hl, 23
jp l6
l6:
ret
;----------------------------------
atoi_return ds 0
atoi_str1 ds 2
atoi_a ds 2
atoi_str ds 2
atoi_c ds 1
atoi:
push bc
ld (atoi_a), hl
; 31 str = str1;
ld hl, (atoi_str1)
ld bc, hl
; 32 do {
l8:
; 33 c = ((uchar)a & 15) + 48;
ld a, (atoi_a)
and 15
add 48
ld (atoi_c), a
; 34 if('9' < c) c += 7;
ld a, (atoi_c)
cp 57
jp c, l10
; 34 c += 7;
ld a, (atoi_c)
add 7
ld (atoi_c), a
l10:
; 35 *str = c; str++;
ld a, (atoi_c)
ld (bc), a
; 35 str++;
inc bc
; 36 a = a >> 4;
ld de, 4
ld hl, (atoi_a)
call shr_ushort
ld (atoi_a), hl
ld hl, (atoi_a)
ld a, l
or h
jp nz, l8
; 38 *str = 0;
xor a
ld (bc), a
l7:
pop bc
ret
;----------------------------------
main_return ds 0
main_i ds 1
main_x ds 1
main_buf ds 32
main:
push bc
; 47 clrscr();
call clrscr
; 48 print(0, 0, "HELLO");
xor a
ld (print_x), a
xor a
ld (print_y), a
ld hl, string0
call print
; 49 atoi(buf, 0x12AB);
ld hl, main_buf
ld (atoi_str1), hl
ld hl, 4779
call atoi
; 51 print(1, 1, buf);
ld a, 1
ld (print_x), a
ld a, 1
ld (print_y), a
ld hl, main_buf
call print
; 53 i=5;
ld b, 5
; 55 for(x=0; x<64; x+=XYZ(12)) {
ld c, 0
l12:
ld a, c
cp 64
jp nc, l13
l12:
; 56 print(x, i, "12345678.123");
ld a, c
ld (print_x), a
ld a, b
ld (print_y), a
ld hl, string1
call print
ld a, c
add 14
ld c, a
jp l12
l13:
; 59 while(1) {
l15:
; 60 if(getch() == '1') clrscr();
call getch
cp 49
jp nz, l18
; 60 clrscr();
call clrscr
l18:
jp l15
l16:
l11:
pop bc
ret
1888 строк 54 Кб.
---------- Post added at 03:23 ---------- Previous post was at 02:47 ----------
Мне написали, что есть компиляторы Си для 8080.
https://github.com/begoon/smallc-scc3, https://github.com/begoon/smallc-85
и работают они так
Код:main() {
static char a;
for (a = '\0'; a < '\10'; ++a) {
a = a + '\1';
}
}
(специально использовал char везде) генерит адъ типа:
;main() {
main:
; static char a;
dseg
?2: ds 1
cseg
; for (a = '\0'; a < '\10'; ++a) {
lxi h,?2
push h
lxi h,0
pop d
call ?pchar
?3:
lxi h,?2
call ?gchar
push h
lxi h,12592
pop d
call ?lt
mov a,h
ora l
jnz ?5
jmp ?6
?4:
lxi h,?2
push h
call ?gchar
inx h
pop d
call ?pchar
jmp ?3
?5:
; a = a + '\1';
lxi h,?2
push h
lxi h,?2
call ?gchar
push h
lxi h,49
pop d
dad d
pop d
call ?pchar
; }
jmp ?4
?6:
;}
?1:
ret
мой
main:
push bc
; 3 for (a = 0; a < 10; ++a) {
ld b, 0
l1:
ld a, b
cp 10
jp nc, l2
l1:
; 4 a = a + 1;
ld a, b
inc a
ld b, a
inc b
jp l1
l2:
l0:
pop bc
ret
Ну далеко не всегда нужно все переменные копировать в стек. В идеале нужно вести время жизни переменных. Часть переменных уже не используется, часть ещё не присвоены, часть будут безусловно проинициализированы после вызова, и текущее значение не нужно.
А это зачем?
ld hl, (clrscr_dest)
ld (hl), a
Регистр а можно прямо поместить по нужному адресу.
Кстати, 0xE1D5 + x можно ещё оптимальнее
Вместо
ld hl, (print_x)
ld h, 0
ld de, 57813 // 0xE1D5
add hl, de
Сделать
ld hl, (print_x)
ld de, 0x0СD5 // d=E1-D5=0C
ld h, е // На байт короче, на 3 такта быстрее
add hl, de
А вот здесь у тебя неверный код, вроде
while(c--);
dec bc
ld a, b
or c
jp nz, l5
У тебя код не постдекрементный, а преддекрементный. Т.е. для случая while(--c), а не while(c--).
Переменные на стеке нужны для реентабельности процедур. Это не только рекурсия, но и второе в хождение в ту же процедуру из обработчика перерывания. Соответственно, в случае работы системы с прерываниями, копировать глобальные переменные на стек бессмысленно - остается вероятность что по прерыванию будет повторное вхождение пока переменные еще не скопированы. Ну и если будет такое копирование, всякий выигрыш теряется (что про времени выполнения, что по размеру кода). Тогда уж сразу надо делать на стеке.
Насчёт реентабельности по прерываниям - да, абсолютно верно, такие процедуры нереентабельные. А вот с "Ну и всякий выигрыш теряется (что про времени выполнения, что по размеру кода)" я не согласен. Для 8080 (не Z80) операции со стеком весьма и весьма мудоёмкие, сжирающие как минимум одну из трёх регистровых пар (HL), которая весьма незаменима, а это постоянные прологи из двух-пяти команд для каждого обращения к стеку. Выигрыш весьма существенный. Особенно в тех случаях, когда возможен анализ кода, есть ли возможность рекурсии при вызове некой функции.
-
Проблема нереентабельности по прерываниям, как по мне, несущественная. Обработчик обычно пишется на ассемблере, если какие-то сишные функции и будут вызываться - то обычно это не те, что выполняются вне прерывания. Этот факт нужно отразить в документации, и принять его "как есть".
-
Более серьёзная проблема, как по мне - это передача ссылки на локальную переменную в некую функцию. Если создаются копии локальных переменных в стеке, то ссылка на локальную переменную "повиснет". А если ещё добавить, что вызываемая функция эту ссылку может сохранить в глобальной переменной, и другая вызываемая функция (из базовой функции) эту ссылку может оттуда поднять... Тут весьма сложный момент. И врядли он будет сделан в соответствии со стандартами языка. Я не хочу сейчас грузить vinxru этой проблемой. Он и так молодец.
В ячейке clrscr_dest хранится адрес, который предыдущей строкой увеличивается.
Нет на 8080 команды ld ((clrscr_dest)), A
А вот на PDP-11 есть.
---------- Post added at 14:31 ---------- Previous post was at 14:30 ----------
Да, я пока одну версию операторов написал. Сегодня поправлю, а то забуду.
---------- Post added at 14:34 ---------- Previous post was at 14:31 ----------
Чисто арифметически выигрыш есть. Копируем мы переменную один раз на входе, один на выходе. Причем, копирование всех переменных можно делать за один присест.
А обращаться к переменной мы будем много раз, может быть что 1000 раз.
---------- Post added at 14:35 ---------- Previous post was at 14:34 ----------
Можно продублировать функцию вызываемую из прерывания и из основного кода.
---------- Post added at 14:37 ---------- Previous post was at 14:35 ----------
Это компромисс между
1) Программой, которая умеет рекурсию.
2) И программой, которая работает в 3 раза быстрее.