Просмотр полной версии : Кросскомпилятор Си под 8080
Покажите пальцем, где можно скачать хороший компилятор Си для процессора 8080, который бы я мог запустить под Виндузом.
Что то вдруг захотелось написать меню для Апогея на Си.
Есть SDCC, но попадает ли он в категорию "хороший", трудно сказать.
---------- Post added at 14:25 ---------- Previous post was at 14:24 ----------
Блин, забыл, что он для z80 :)
---------- Post added at 14:26 ---------- Previous post was at 14:25 ----------
Можно запускать сипиэмовский компилятор в консольном эмуляторе.
Я даже поискал варианты C -> LLVM -> 8080. То же пусто.
http://www.z80.eu/c-compiler.html
из того что было на корвете -
BDS-C
Aztec C 1.06d
автор BDS-C раздат исходники
http://www.bdsoft.com/resources/bdsc.html
автор BDS-C раздат исходники
http://www.bdsoft.com/resources/bdsc.html
На ассемблере 8080 :)
ой, кросс не заметил ;)
можно в cp/m эмуляторе пускать
правда думаю код там ....
Есть у меня страшная мысль...
vinxru, можешь попробовать BDS C в браузере =)
http://sensi.org/~svo/i8080/c
Все-таки на 8080 Си как-то плохо ложится. Очень уж чугунный процессор.
Все-таки на 8080 Си как-то плохо ложится. Очень уж чугунный процессор.
Даладно. Можно же только глобальные переменные использовать :)
Даладно. Можно же только глобальные переменные использовать
Да. Я решил проверить, могу ли написать базовую часть компилятора за час. Без структур, препроцессора, указателей на функции, оптимизатора. Написал.
Но уткнулся в то, что работа со стековыми переменными уж очень медленная. И хрен знает как под это оптимизатор писать.
ushort a,b,c;
a = b+c;
; push16 B
LD HL, 4
ADD HL, SP
LD B, (HL)
INC HL
LD C, (HL)
; push16 c
LD HL, 6
ADD HL, SP
LD E, (HL)
INC HL
LD D, (HL)
; add16
EX HL, DE
ADD HL, BC
EX HL, DE
; pop16 a
LD HL, 2
ADD HL, SP
LD (HL), E
INC HL
LD (HL), D
Надо писать си, где все переменные глобальные, если нет рекурсии.
predatoralpha
11.09.2012, 16:12
Да. Я решил проверить, могу ли написать базовую часть компилятора за час. Без структур, препроцессора, указателей на функции, оптимизатора. Написал.
Но уткнулся в то, что работа со стековыми переменными уж очень медленная. И хрен знает как под это оптимизатор писать.
ushort a,b,c;
a = b+c;
; push16 B
LD HL, 4
ADD HL, SP
LD B, (HL)
INC HL
LD C, (HL)
; push16 c
LD HL, 6
ADD HL, SP
LD E, (HL)
INC HL
LD D, (HL)
; add16
EX HL, DE
ADD HL, BC
EX HL, DE
; pop16 a
LD HL, 2
ADD HL, SP
LD (HL), E
INC HL
LD (HL), D
Надо писать си, где все переменные глобальные, если нет рекурсии. Как вариант - помнить, на какую ячейку стека ссылался HL в прошлый раз. И просто прибавлять/вычитать значение, а не лопатить постоянно LD HL, 6 ADD HL, SP
Гений, который проектировал 8080, забыл сделать все нужные для человеческой жизни виды адресаций, зато не забыл XTHL. По-моему XTHL предположительно должен как-то помогать извлечению барахла из стека, но я так и не смог сообразить, как.
vinxru, не бросай. Если бы был компилятор Си для 8080, который бы без эмуляторных оберток работал на современной системе, это было бы уже очень хорошо. Даже просто как обертка для ассемблерных вставок он бы уже был полезен. Только структуры поддержи ;)
Vladimir_S
11.09.2012, 16:20
vinxru, прочитал название темы и аж сердце екнуло.А оказывается все только начинается.
Как вариант - помнить, на какую ячейку стека ссылался HL в прошлый раз. И просто прибавлять/вычитать значение, а не лопатить постоянно LD HL, 6 ADD HL, SP
lxi h, 6 dad sp -- это и есть просто прибавить значение.
Надо сначала ром диск закончить, а потом будем делать компилятор. Тем более, что меня далеко не раз просили. Например фанаты компьютера Львов ПК-01.
---------- Post added at 15:33 ---------- Previous post was at 15:31 ----------
Я так подумал. Можно быстро сделать, если исключить две фишки
1) Рекурсию
2) Указатели на функции.
Тогда адреса стека (и глубину стека) можно легко рассчитать. Приведенный выше код будет выглядеть как
LD DE, A
LD HL, B
ADD HL, DE
LD C, HL
predatoralpha
11.09.2012, 18:06
lxi h, 6 dad sp -- это и есть просто прибавить значение. Я имел ввиду не прибавлять к sp, а модифицировать только HL - ADD HL, nnn. Первый раз надо сделать так, как есть. Потом - просто менять HL на некоторое значение.
Вместо двух lxi h, 6 dad sp остаётся один add
ПС. Извиняюсь, пересмотрел систему команд, прибавления к регистровой паре значения нету. Вопрос снят.
Приведенный выше код будет выглядеть как
LD DE, A
LD HL, B
Наверно я туплю, но A и B разве константы? Если это глобальные переменные, то д.б. что-то вроде
lhld usA
xchg
lhld usB
Блин. Есть у меня проблема с постепенным расширением ТЗ.
В общем эта программа
void print(uchar x, uchar y, uchar* text) {
uchar* d;
d = (uchar*)(0xA000 + ((ushort)x) + ((ushort)y) * 78); // Автопреобразования пока нет.
while(*text) { *d = *text; d++; text++; }
}
void main() {
print(5, 5, "HELLO");
print(5, 7, "WORLD");
}
на выходе
print_text dw 0 ; Переменные разных функций будут занимать одну память.
print_d dw 0
print_x db 0
print_y db 0
print:
ld de, 78
ld hl, (print_y)
ld h, 0
call mul_ushort
ex de, hl
ld hl, (print_x)
ld h, 0
add hl, de
ld de, 40960
add hl, de
ld (print_d), hl
l0:
ld hl, (print_text)
ld a, (hl)
or a
jp z, l1
ld hl, (print_d)
ld (hl), a
inc hl
ld (print_d), hl
ld hl, (print_text)
inc hl
ld (print_text), hl
jp l0
l1:
ret
main:
ld a, 5
ld (print_x), a
ld (print_y), a
ld hl, text1 ; "HELLO"
ld (print_text), hl
call print
ld a, 5
ld (print_x), a
ld a, 7
ld (print_y), a
ld hl, text2 ; "WORLD"
ld (print_text), hl
call print
ret
---------- Post added at 17:58 ---------- Previous post was at 17:56 ----------
Регистровая пара BC не используется. Её можно будет использовать для хранения какого то значения.
---------- Post added at 18:00 ---------- Previous post was at 17:58 ----------
А вот такая конструкция
while(*text) *d++ = *text++;
раскрывается в
l0:
ld hl, (print_text)
ld a, (hl)
or a
jp z, l1
ld hl, (print_d)
ld (tmp3), hl
inc hl
ld (print_d), hl
ld hl, (print_text)
ld (tmp4), hl
inc hl
ld (print_text), hl
ld hl, (tmp4)
ld a, (hl)
ld hl, (tmp3)
ld (hl), a
jp l0
l1:
Для 8080 было бы здорово генерить мнемоники 8080. Тут я не могу даже оценить насколько хорош код -- хоть я смысл мнемоник и знаю, в глазах одно сплошное LD.
Операции копирования строк в 8080 -- самая больная тема. Есть еще ldax rp/stax rp, может быть BC пригодится? Хотя по-моему гнаться за этим не стоит, можно написать memmove сколь угодно оптимальный на ассемблере.
может нафиг эти C-Strings
постоянное место для граблей :(
Получилось 150 строк кода, для программы преобразующей синтаксис Си и байткод. И там почти все закончено. И 300 строк для байткода. Причем реализовано лишь 5% всех функций байткода.
...
void bindVar(CType& type) {
// Чтение монооператора, выполнять будет потом
vector<MonoOperator> mo;
while(true) {
if(p.ifToken("*")) { mo.push_back(moDeaddr); continue; }
if(p.ifToken("&")) { mo.push_back(moAddr); continue; }
if(p.ifToken("!")) { mo.push_back(moNot); continue; }
if(p.ifToken("-")) { mo.push_back(moNeg); continue; }
break;
}
bindVar_2(p, type);
while(true) {
if(p.ifToken("[")) {
CType type1;
readVar(p, -1, type1);
p.needToken("]");
if(type1.addr!=0) raise("[]");
asm_index(type);
continue;
}
if(p.ifToken("++")) { asm_callMonoOperator(moPostInc, type); continue; }
if(p.ifToken("--")) { asm_callMonoOperator(moPostDec, type); continue; }
break;
}
// Вычисление моно операторов
for(int i=0; i<mo.size(); i++)
asm_callMonoOperator(mo[i], type);
}
const char_t* operators [] = { "++", "--", "/", "%", "*", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", "?", "=", "+=", "-=", "*=", "/=", "%=", ">>=", "<<=", "&=", "^=", "|=", 0 };
int operatorsP[] = { 13, 13, 12, 12, 12, 11, 11, 10, 10, 9, 9, 9, 9, 8, 8, 7, 6, 5, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Operator operatorsI[] = { oInc, oDec, oDiv,oMod,oMul,oAdd,oSub,oShl, oShr, oL, oG, oLE, oGE, oE, oNE, oAnd, oXor, oOr, oLAnd, oLOr, oIf, oSet,oSAdd,oSSub,oSMul,oSDiv,oSMod,oSShl, oSShr, oSAnd,oSXor,oXOr };
void readVar(int level, CType& type) {
// Чтение аргумента
bindVar(type);
// Чтение оператора
while(true) {
int l=0;
Operator o = findOperator(p, level, l);
if(o == oNone) return;
//! Оптимизировать проверку условий.
//! Команда ? :
// Чтение второго аргумента
CType b_type;
readVar(p, l, b_type);
// Выполнение опертора
asm_callOperator(o, type, b_type);
}
}
void readCommand() {
if(p.ifToken(";")) return;
if(p.ifToken("while")) {
p.needToken("(");
CType type;
readVar(p, -1, type);
int label1 = labelesCnt++;
int label2 = labelesCnt++;
asm_label(label1);
asm_jz(label2, type);
p.needToken(")");
readCommand(p);
asm_jmp(label1);
asm_label(label2);
return;
}
if(p.ifToken("if")) {
p.needToken("(");
CType type;
readVar(p, -1, type);
p.needToken(")");
int label1 = labelesCnt++;
asm_jz(label1, type);
readCommand(p);
if(p.ifToken("else")) {
int label2 = labelesCnt++;
asm_jmp(label2);
asm_label(label1);
readCommand(p);
asm_label(label2);
} else {
asm_label(label1);
}
return;
}
...
}
---------- Post added at 19:46 ---------- Previous post was at 19:43 ----------
А вот кусок преобразователя байт код - асм, на который не одну неделю придется убить.
void pushHL(int l, bool de=false) {
Stack& bs = stack[stack.size()-1-l];
switch(bs.place) {
case pConst: code.str(de ? " ld de" : " ld hl").str(", ").i2s(bs.value).str("\r\n"); break;
case pVar: code.str(" ld hl, (").str(bs.name).str(")\r\n");
if(de) code.str(" ex de, hl\r\n");
break;
case pVar8: code.str(" ld hl, (").str(bs.name).str(")\r\n");
code.str(" ld h, 0\r\n");
if(de) code.str(" ex de, hl\r\n");
break;
case pRef: code.str(" ld hl, (").str(bs.name).str(")\r\n");
if(!de) {
code.str(" ld a, (hl)\r\n");
code.str(" inc hl\r\n");
code.str(" ld h, (hl)\r\n");
code.str(" ld l, a\r\n");
} else {
code.str(" ld e, (hl)\r\n");
code.str(" inc hl\r\n");
code.str(" ld d, (hl)\r\n");
}
break;
case pConstRef: code.str(" ld hl, (").i2s(bs.value).str(")\r\n");
if(de) code.str(" ex de, hl\r\n");
break;
default: raise("3");
}
}
void asm_callOperator(Operator o, CType& a, CType b) {
Stack& as = stack[stack.size()-2];
Stack& bs = stack[stack.size()-1];
// Преобразование константы USHORT в UCHAR
if(a.baseType==cbtUChar && b.baseType==cbtUShort && bs.place==pConst) {
if(bs.value>=0 && bs.value<=0xFF) b = cbtUChar;
}
if(a.baseType!=b.baseType || a.addr !=b.addr)
raise("asm_callOperator type");
if(o==oSet) {
if(a.baseType==cbtUChar && a.addr==0) {
pushA(0);
switch(as.place) {
case pConst: raise("Нельзя изменить константу"); break;
case pRef: code.str(" ld hl, (").str(as.name).str(")\r\n");
code.str(" ld (hl), a\r\n\r\n"); break;
case pVar: code.str(" ld (").str(as.name).str("), a\r\n\r\n"); break;
case pConstRef: code.str(" ld (").i2s(as.value).str("), a\r\n\r\n"); break;
default: raise("2");
}
stack.pop_back();
return;
}
if((a.baseType==cbtUShort || a.baseType==cbtShort) || a.addr>0) {
pushHL(0);
switch(as.place) {
case pConst: raise("Нельзя изменить константу"); break;
case pRef: code.str(" ex hl, de\r\n");
code.str(" ld hl, (").str(as.name).str(")\r\n");
code.str(" ld (hl), e\r\n");
code.str(" inc hl\r\n");
code.str(" ld (hl), d\r\n\r\n"); break;
case pVar: code.str(" ld (").str(as.name).str("), hl\r\n\r\n"); break;
case pConstRef: code.str(" ld (").i2s(as.value).str("), hl\r\n\r\n"); break;
default: raise("4");
}
stack.pop_back();
return;
}
raise("xxx");
return;
}
Вчера скомпилировал первые программы. Мне этого функционала пока хватит для написания меню. А по мелочи добавлю.
Ключевые слова break, continue, return, switch, default, do {} while, union, typedef, extern, sizeof не реализованы.
Описание внешних функций (прототипы), переменных не реализованы. Например: int myFunction(int); extern int a;
Арифметика реализована только для типов uchar, ushort. Только операции +, -, ++, --, +=, -=, |=, &=, ^=, *, /, =, |, &, ^, <. Например сдвигов пока нет. Операции с типами ulong, long производить пока нельзя.
Инициализации статических переменных нет Например: char data[] = { 0x10, 0x20 }; FileInfo files[] = { { "abc", 1 }, { "def", 2 } };
Типы данных real, float, double, const, static, register, volatile, auto не реализованы. Слово register будет заставлять размещать переменную в регистре BC (или B или C).
Препроцессор не реализован, хотя там 5 минут работы. Например: #include, #define, #ifdef, #ifndef, #endif
Вставки ассемблера не реализованы.
Грамотный учет временных переменных не реализован. Резервирует больше памяти, чем нужно. Вообще переделаю на push hl, pop hl
Контроля рекурсии нет и необходимый размер стека не определяется. Рекурсия кстати будет. При рекурсивном вызове функции, все переменные используемые предыдущим вызовом функции будут копироваться в стек, а новая функция будет работать с фиксированной памятью. Не идеальный способ, но относительно быстрый. Будут проблемы при использовании указателей на локальные переменные.
Функции сравнения не оптимизированы. Сейчас первым этапом сравнения вычисляется значение True, False, а уже потом оно анализируется функцей перехода.
Сравнение
ld a, (var1)
ld hl, (var2)
cp (hl)
sbc a
Переход
or a
jp z, label
predatoralpha
12.09.2012, 11:52
А как обстоят дела с макросами и #include ??
Кстати, do {} while может быть и без фигурных скобок.
do ;
while (i--);
корректный пример организации задержки.
Вообще, байткод получившегося Си очень сильно напоминает PDP11. (Что не удивительно). Все адресации переменных копируют адресацию PDP11.
Но система команд 8080 за исключением некоторых моментов то же выглядит вполне логичной, если не использовать стек.
Для всех двухадресных 16 битных команд регистр HL используется для хранения основного значения, DE для второго. Команды "EX HL, DE", "LD HL, IMM", "LD HL, (ADDR)", "ADD HL, DE", "LD (ADDR), HL" используются постоянно. Традиционно: XCHG, LHLD, LXI H, DAD D, SHLD
addr = (y*78 + 0xA000 + x - offset ) | 1234h;
; y*78
LD A, (Y)
LD D, 78
CALL MUL_UCHAR8 ; Вход A,D. Выход HL
; +0xA000
LD DE, 0A000h
ADD HL, DE
; +x
EX HL, DE
LD HL, (x) ; 8 бит
LD H, 0
ADD HL, DE
; -offset
EX HL, DE
LD HL, (offset)
LD A, E
SUB L
LD L, A
LD A, D
SBC H
LD H, A
; | 1234h
LD A, L
OR 34h
LD L, A
LD A, H
OR 12h
LD H, A
; addr=
LD (addr), HL
Но все таки не понятно, почему команды
LD BC, IMM16
LD HL, IMM16
есть, а
LD DE, нет
LD BC, IMM16
LD HL, IMM16
есть, а
LD DE, нет
Как это нет?
Ой, есть :)
---------- Post added at 11:18 ---------- Previous post was at 11:15 ----------
А как обстоят дела с макросами и #include ??
Парсер их поддерживает, надо только пару строк написать.
---------- Post added at 11:23 ---------- Previous post was at 11:18 ----------
do ;
while (i--);
корректный пример организации задержки.
Да, у меня там DO <команда> WHILE(<вражение>)
Можно так писать: do a++, b++, c++; while(c<10);
Вчера во время отладки, перебирая все варианты, написал:
while(a==1) {
a=2;
} while(a==2);
И долго пялился. :)
predatoralpha
12.09.2012, 12:24
Но все таки не понятно, почему команды
LD BC, IMM16
LD HL, IMM16
есть, а
LD DE, нет
А точно нет? В Z80 всем этим командам соответствует опкод
00 dd0 001
Где dd для BC=00; DE=01; HL=10; SP=11
Может в 8080 неверно декодируется команда, когда младший бит dd==1, потому команды так и не появились..
ПС. Вопрос снят.....
Ступил, я вчера думал про команду
LD DE, (ADDR)
Вот в этом куске не нужна была бы команда
EX HL, DE
LD HL, (offset)
LD A, E
SUB L
LD L, A
LD A, D
SBC H
LD H, A
Аналогично
EX HL, DE
LD HL, (offset)
ADD HL, DE
---
Традиционно:
XCHG
LHLD offset
MOV A, E
SUB L
MOV L, A
MOV A, D
SBB H
MOV H, A
Аналогично
XCHG
LHLD offset
DAD D
---------- Post added at 11:48 ---------- Previous post was at 11:28 ----------
По сравнению с PDP11 топорно очень
*a += *b;
LD HL, (B)
LD E, (HL)
INC HL
LD D, (HL)
LD HL, (A)
LD A, (HL)
INC HL
LD H, (HL)
LD L, A
ADD HL, DE
EX HL, DE
LD HL, (A)
LD (HL), E
INC HL
LD (HL), D
(Можно использовать команды 8-битной арифметики сократить программу на пару строк, но не суть).
или на PDP11
ADD @(A), @(B)
predatoralpha
12.09.2012, 13:22
Вот в этом куске не нужна была бы команда
EX HL, DE
LD HL, (offset)
LD A, E
SUB L
LD L, A
LD A, D
SBC H
LD H, A
Аналогично
EX HL, DE
LD HL, (offset)
ADD HL, DE
Тут разве что интеллектуально делать первым не HL, а DE. И команды сложения производить с ним, накапливая в нём результат. Тогда можно обойтись без обмена.
ПС. Хотя нет, арифметика работает только с HL. Блин, уже основательно подзабыл ассемблер Z80...
Вот в этом куске не нужна была бы команда
[COLOR="Red"]или на PDP11
ADD @(A), @(B) Да, удобная вещь. Я работал на DSP, там есть команды индексной загрузки с пост- и пред- инкрементом/декрементом, очень быстро работает перебор.
Переписал арифметику с нуля. Вместо временных переменных используется стек.
; 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 (print_dest), hl
Основной код пока выглядит еще не очень. Нужно сделать хранение заначений в регистре BC и доделать сравнение (команда CP вместо вызова call e_uchar). И for парсить нелинейно, что бы код был линейным :)
main_i ds 1
main_x ds 1
main_buf ds 32
main:
; clrscr();
call clrscr
; print(0, 0, "HELLO");
xor a
ld (print_x), a
ld (print_y), a
ld hl, string0
call print
; itoa(buf, 123);
ld hl, main_buf
ld (itoa_str), hl
ld hl, 123
call itoa
; print(1, 1, buf);
ld a, 1
ld (print_x), a
ld (print_y), a
ld hl, main_buf
call print
; i = 5;
ld a, 5
ld (main_i), a
; 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:
; print(x, i, "12345678.123");
ld a, (main_x)
ld (print_x), a
ld a, (main_i)
ld (print_y), a
ld hl, string1
call print
; }
jp l13
l11:
; while(1) {
l14:
; if(getch() == '1') clrscr();
call getch
ld d, 49
call e_uchar
or a
jp z, l16
call clrscr
l16:
jp l14
l15:
l9:
ret
;----------------------------------
string1 db "12345678.123",0
string0 db "HELLO",0
---------- Post added at 23:56 ---------- Previous post was at 22:47 ----------
1500 строк, 45 Кб
Error404
13.09.2012, 09:24
Как-то совсем непродуманно выглядит передача параметров в функцию.
Всмысле?
Все переменные глобальные, в том числе и аргументы функций.
Только я сделал небольшую оптимизацию, что последний параметр записывается не в память, а в регистры A или HL. А первой командой в вызываемой функции параметр записывается в память.
---------- Post added at 10:22 ---------- Previous post was at 10:17 ----------
Сделал хранение переменных в регистрах BC, B, C. Ключевое слово register. Операцию >>.
Error404
13.09.2012, 11:52
Всмысле?
Считаю было бы стратегически правильным сразу делать шедевр.
Т.е. фукнциональность (набор операторов) - бог с ней, потом допишется, кодогенератор аналогично пока можно не оптимизировать. А вот базовые механизмы надо бы внедрить изначально. Позже это делать будет сложнее и приведет к размножению костылей и как следствие багов. Соответственно надо запланировать все нужные типы, арифметику, передачу параметров на стеке (от которых и до локальных переменных функций - полшага).
Если делать через стек, то программа будет работать еще в 3 раза дольше.
Для стека
LD HL, ADDR16
ADD HL, SP
LD A, (HL)
или
LXI H, ADDR16
DAD SP
MOV A, M
Глобальные переменные.
LD A, (ADDR16)
или
LDA ADDR16
---------- Post added at 11:10 ---------- Previous post was at 11:04 ----------
Может мне кто нибудь подкинет функции умножения, деления, вычисления остатка от деления для 8, 16 бит, знаковых и без знаковых типов? :)
Вот умножение, умножает DE на HL, результат в HL:
_IMUL: MOV B,H
MOV C,L
LXI H,0
MVI A,17
_IMUL1: DCR A
RZ
DAD H
XCHG
DAD H
XCHG
JNC _IMUL1
DAD B
JMP _IMUL1
Несложно доделать, чтобы результат был 32-битным в DEHL:
_IMUL: MOV B,H
MOV C,L
LXI H,0
MVI A,17
_IMUL1: DCR A
RZ
DAD H
XCHG
JC _IMUL2
DAD H
XCHG
JNC _IMUL1
DAD B
JMP _IMUL1
_IMUL2: DAD H
INX H
XCHG
JNC _IMUL1
DAD B
JMP _IMUL1
---------- Post added at 13:36 ---------- Previous post was at 13:32 ----------
Для знаковых, перед умножением надо проверить, если оба числа отрицательные, то сделать оба положительными. Отрицательное на положительное можно и так умножать.
---------- Post added at 13:53 ---------- Previous post was at 13:36 ----------
Беззнаковое деление/остаток:
_DIV: MOV A,H
ORA L
RZ
LXI B,0000
PUSH B
_DIV1: MOV A,E
SUB L
MOV A,D
SBB H
JC _DIV2
PUSH H
DAD H
JNC _DIV1
_DIV2: LXI H,0000
_DIV3: POP B
MOV A,B
ORA C
RZ
DAD H
PUSH D
MOV A,E
SUB C
MOV E,A
MOV A,D
SBB B
MOV D,A
JC _DIV4
INX H
POP B
JMP _DIV3
_DIV4: POP D
JMP _DIV3
DE делится на HL, результат в HL, остаток в DE
---------- Post added at 14:00 ---------- Previous post was at 13:53 ----------
Для знаковых, перед умножением надо проверить, если оба числа отрицательные, то сделать оба положительными. Отрицательное на положительное можно и так умножать.
Если использовать только 16-битный результат, то можно даже не проверять на знак :)
---------- Post added at 14:09 ---------- Previous post was at 14:00 ----------
А если использовать 32-битный результат, то надо классически проверить знаки, привести к положительным значениям, и потом, если надо, инвертировать знак результата :rolleyes:
чтобы результат был 32-битным в DEHL
Приведенный вариант не совсем правильный. Попробуйте умножить 7FFF*7FFF. Можно после dad b (оба раза) вставить
jnc _IMUL1
inx d
а потом уже JMP _IMUL1
Приведенный вариант не совсем правильный. Попробуйте умножить 7FFF*7FFF. Можно после dad b (оба раза) вставить
jnc _IMUL1
inx d
а потом уже JMP _IMUL1
Согласен. Я просто не проверял этот вариант, упустил из виду, что DAD B тоже может перенос вызвать.
---------- Post added at 17:08 ---------- Previous post was at 17:05 ----------
Вот, поправил:
_IMUL: MOV B,H
MOV C,L
LXI H,0
MVI A,17
_IMUL1: DCR A
RZ
DAD H
XCHG
JNC _IMUL2
DAD H
INX H
JMP _IMUL3
_IMUL2: DAD H
_IMUL3: XCHG
JNC _IMUL1
DAD B
JNC IMUL1
INX D
JMP _IMUL1
Команда DEX B не изменяет флагов вообще, и я думал, что и DAD то же не изменяет.
Значит её можно в циклах применять
ADD HL, DE
JP NC, LOOP
вместо
DEC HL
LD A, H
OR L
JP NZ, LOOP
Значит её можно в циклах применять
Но при этом тебе надо будет в DE еденицу грузить. 4 байта против трёх.
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_8080/%D0%9A%D0%BE%D0%B4%D1%8B_%D0%BA%D0%BE%D0%BC%D0%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
http://www.emuverse.ru/wiki/Intel_8080/%D0%9A%D0%BE%D0%B4%D1%8B_%D0%BA%D0%BE%D0%BC%D0%B0% D0%BD%D0%B4
DAD = 4 такта
Опечатка. Даташит не врёт :)
predatoralpha
14.09.2012, 00:21
Я тут просматривал код:
; 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 (как полагается с параметрами).
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
В этом коде еще убрать дублирующиеся присваивания переменных (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.
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
predatoralpha
14.09.2012, 13:15
Хм, сделаю и такой режим :) Но когда переменные лежат одним блоком, легко реализовать рекурсию. Просто этот блок переменных копировать в стек. Ну далеко не всегда нужно все переменные копировать в стек. В идеале нужно вести время жизни переменных. Часть переменных уже не используется, часть ещё не присвоены, часть будут безусловно проинициализированы после вызова, и текущее значение не нужно.
А это зачем?
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--).
Error404
14.09.2012, 14:19
Переменные на стеке нужны для реентабельности процедур. Это не только рекурсия, но и второе в хождение в ту же процедуру из обработчика перерывания. Соответственно, в случае работы системы с прерываниями, копировать глобальные переменные на стек бессмысленно - остается вероятность что по прерыванию будет повторное вхождение пока переменные еще не скопированы. Ну и если будет такое копирование, всякий выигрыш теряется (что про времени выполнения, что по размеру кода). Тогда уж сразу надо делать на стеке.
predatoralpha
14.09.2012, 14:30
Переменные на стеке нужны для реентабельности процедур. Это не только рекурсия, но и второе в хождение в ту же процедуру из обработчика перерывания. Соответственно, в случае работы системы с прерываниями, копировать глобальные переменные на стек бессмысленно - остается вероятность что по прерыванию будет повторное вхождение пока переменные еще не скопированы. Ну и всякий выигрыш теряется (что про времени выполнения, что по размеру кода) если будет такое копирование: тогда уж сразу надо делать на стеке.
Насчёт реентабельности по прерываниям - да, абсолютно верно, такие процедуры нереентабельные. А вот с "Ну и всякий выигрыш теряется (что про времени выполнения, что по размеру кода)" я не согласен. Для 8080 (не Z80) операции со стеком весьма и весьма мудоёмкие, сжирающие как минимум одну из трёх регистровых пар (HL), которая весьма незаменима, а это постоянные прологи из двух-пяти команд для каждого обращения к стеку. Выигрыш весьма существенный. Особенно в тех случаях, когда возможен анализ кода, есть ли возможность рекурсии при вызове некой функции.
-
Проблема нереентабельности по прерываниям, как по мне, несущественная. Обработчик обычно пишется на ассемблере, если какие-то сишные функции и будут вызываться - то обычно это не те, что выполняются вне прерывания. Этот факт нужно отразить в документации, и принять его "как есть".
-
Более серьёзная проблема, как по мне - это передача ссылки на локальную переменную в некую функцию. Если создаются копии локальных переменных в стеке, то ссылка на локальную переменную "повиснет". А если ещё добавить, что вызываемая функция эту ссылку может сохранить в глобальной переменной, и другая вызываемая функция (из базовой функции) эту ссылку может оттуда поднять... Тут весьма сложный момент. И врядли он будет сделан в соответствии со стандартами языка. Я не хочу сейчас грузить vinxru этой проблемой. Он и так молодец.
ld hl, (clrscr_dest)
ld (hl), a
В ячейке clrscr_dest хранится адрес, который предыдущей строкой увеличивается.
Нет на 8080 команды ld ((clrscr_dest)), A
А вот на PDP-11 есть.
---------- Post added at 14:31 ---------- Previous post was at 14:30 ----------
А вот здесь у тебя неверный код, вроде
while(c--);
dec bc
ld a, b
or c
jp nz, l5
У тебя код не постдекрементный, а преддекрементный. Т.е. для случая while(--c), а не while(c--).
Да, я пока одну версию операторов написал. Сегодня поправлю, а то забуду.
---------- 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 раза быстрее.
predatoralpha
14.09.2012, 16:50
В ячейке clrscr_dest хранится адрес, который предыдущей строкой увеличивается.
Нет на 8080 команды ld ((clrscr_dest)), A Извиняюсь, просмотрел, что тут двойной доступ.
Да, я пока одну версию операторов написал. Сегодня поправлю, а то забуду.Догадываюсь, что код сильно усложнится. Прединкремент/преддекремент работает быстрее, и код короче. Конкретно здесь, поскольку переменная с в теле не используется, его стОит оставить, только увеличить на единицу BC (или сразу загрузить в него увеличенную на 1 константу, как в этом случае). Впрочем, анализ циклов сложное дело, и я понимаю, что сейчас оно не на времени.
Можно продублировать функцию вызываемую из прерывания и из основного кода. Безусловно. Кому надо - продублирует.
Это компромисс между
1) Программой, которая умеет рекурсию.
2) И программой, которая работает в 3 раза быстрее. Полностью поддерживаю. Компилятор прежде всего будет использоваться любителями старых компов, типа Львова, а там абсолютный приоритет - быстродействие.
Единственно замечу, что это проблема не только рекурсии. В том примере, что я привёл, это возможно при вызове из функции двух функций, которые никак рекурсию не производят. Просто отразить в документации - если хочешь иметь корректный указатель на лок.перем. между вызовами разных функций - используй указатель на static или volatile переменную.
Это компромисс между
1) Программой, которая умеет рекурсию.
2) И программой, которая работает в 3 раза быстрее.
Так надо ввести опцию компилятора: все переменные статические - да/нет. А компилятор делать классически, с поддержкой локальных переменных в стеке. Кому не нравится глобальная переменная, поставит перед декларацией register.
---------- Post added at 19:26 ---------- Previous post was at 19:23 ----------
И в тексте программы учитывать:
#pragma static_variable on
#pragma static_variable off
predatoralpha
14.09.2012, 18:36
Так надо ввести опцию компилятора: все переменные статические - да/нет. А компилятор делать классически, с поддержкой локальных переменных в стеке. Кому не нравится глобальная переменная, поставит перед декларацией register. Ээээ... ничего что стандарт C99 позволяет указатели на регистровые локальные переменные? И это как раз такой случай, что она должна располагаться в стеке, если конечно компилятор не соптимизирует обращение к переменной, и есть гарантия, что доступа к адресу не будет.
Так что вопрос несколько сложнее, и просто register это не решает.
Здесь планируется несколько более, чем просто статические переменные. Их можно использовать как и автоматические, для рекурсии. Можно сказать, что локальные переменные просто кешируются в памяти по конкретным адресам.
У меня нельзя будет получить адрес регистровой переменной. Как раз сейчас сообщений об ошибках придумывал.
case pVar8: p.logicError_("Нельзя получить адрес переменной преобразованной из 8 бит"); break; // &(ushort)a;
case pA: case pHl: case pStack: p.logicError_("Нельзя получить адрес временного значения"); break;
case pB: case pC: case pBC: p.logicError_("Нельзя получить адрес регистровой переменной"); break;
case pConst: p.logicError_("Нельзя получить адрес константы"); break;
---------- Post added at 19:42 ---------- Previous post was at 19:40 ----------
Кстати первая ошибка - это косяк оптимизации, потом сделаю обход.
Когда мы преобразовываем значение
char a;
short b = (short)a + 0x1234;
Сразу А не преобразуется. Просто функции чтения забивают старшие 8 бит нулями. Это работает везде кроме
short* b = &(short)a;
Оно и так не должно выполняться. А если выполнится, то будет идентично
short* b = (short*)&a;
---------- Post added at 19:43 ---------- Previous post was at 19:42 ----------
Чё я торможу. Это ошибка "Нельзя получить адрес временного значения"
---------- Post added at 20:34 ---------- Previous post was at 19:43 ----------
Сейчас мой мозг вытечет через ухо
Есть абстрактные команды
moNot, moNeg, moAddr, moDeAddr, moPostInc, moPostDec, moInc, moDec
oDiv, oMod, oMul, oAdd, oSub, oShl, oShr, oL, oG, oLE, oGE, oE, oNE, oAnd, oXor, oOr, oLAnd, oLOr, oIf, oSet, oSAdd, oSSub, oSMul, oSDiv, oSMod, oSShl, oSShr, oSAnd, oSXor, oSOr
И такой набор команд на каждый тип данных char, short, long, ulong.
У них несколько значений на входе. Причем значения могут быть:
pConst - это просто число
pConstRef - адрес в памяти
pVar - какая то переменная
pVar8 - переменная, причем используются только 8 нижних бит
pRef - переменная содержащая адрес в памяти
pArray - переменная, но компилятор должен подставлять её адрес в программу вместо значения.
pHl, pA, pB, pC, pBC - регистры
pStack - верхушка стека
pBCRef, pHLRef - в регистрах лежит адрес значения в памяти.
И при этом регистры A, DE, HL используются для временного хранения переменных и их иногда можно использовать, а иногда нельзя.
И при этом, команды должны сваливать результат в
pConstRef - адрес в памяти
pVar - какая то переменная
pRef - переменная содержащая адрес в памяти
pHl, pA, pB, pC, pBC - регистры
pStack - верхушка стека
pBCRef, pHLRef - в регистрах лежит адрес значения в памяти.
---------- Post added at 20:38 ---------- Previous post was at 20:34 ----------
Ой, да. Команды при этом еще объединяются и упрощаются. Например сравнение и переход.
---------- Post added at 21:27 ---------- Previous post was at 20:38 ----------
А чем отличается RAL от ADС A, кроме изменяемых флагов?
У ADC A справа нулевой бит добавляется, а RAL - берёт флаг переноса.
У разве не у ADD нулевой бит добавляется?
Фу-ты, попутал. Значит ничем :)
Команды RAL вообще мне не пригодились
16 битный сдвиг DAD H
8 битный сдвиг ADD A
8 бит в другую сторону CP A + RAR
16 бит вправо самая сложная
MOV A, H/B
CP A
RAR
MOV H, A
MOV A, L/C
RAR
MOV L, A
16 бит вправо самая сложная
MOV A, H/B
CP A
Можно скинуть 1 такт, если первые 2 команды заменить на
xra a (или sub a)
ora h/b
И еще я что то не могу сообразить, как сравнивать знаковые числа. Беззнаковые просто, по флагам Z и C. А знаковые я только придумал
(n1+80h) < (n2+80h)
MOV A, N1
XRI 0x80
MOV D, A
MOV A, N2
XRI 0x80
CP D
А дальше так же по флагам C, Z
Но конструкция получается тормозная и большая. Такую не жалко в подпрограмму вынести, что бы зря размер кода не увеличивать.
А если просто вычесть и знак результата проверить?
MOV A,N1
MOV B,N2
SUB B
JM less
Ааа... спать надо больше. Точно ведь. А мне лень даже в яндекс вбить слово.
---------- Post added at 00:39 ---------- Previous post was at 00:33 ----------
У меня получилось, что myFunction(1, myFuction(2, 3)) некорректно работает
mvi a, 1
sta myFunction_paramA
mvi a, 2
sta myFunction_paramA
mvi a, 3
call myFunction
call myFunction
Но это можно обойти
---------- Post added at 00:40 ---------- Previous post was at 00:39 ----------
В среду дам первую версию. Эта еще сырая очень.
2100 строк 66 Кб
Это компромисс между
1) Программой, которая умеет рекурсию.
2) И программой, которая работает в 3 раза быстрее.
И всё-таки по причинам реентерабельности, стоит подумать о размещении локальных переменных на стеке. Дело здесь не только в interrupt функциях, для которыз кстати надо ещё PUSHALL делать. Просто, если компилятор будет использоваться для написания оконных интерфейсов, почти неизбежны локальные стеки и переключения контекстов.
Возможно, Вы подумаете о чём-нибудь типа
#pragma recursion:ON/OFF
Также пожелание поддерживать несколько calling conventions, по крайей мере extern "C" / _stdcall, _pascal, _memblock (это-Ваш вариант) и _fast (передача до 2х параметров в регистрах)
Сделаю рекурсию. Добавлю модификаторы _static, _stack. Пока не до этого, пока у меня даже комментарии не поддерживаются. :)
---------- Post added at 01:22 ---------- Previous post was at 00:52 ----------
Прикольно. Написал
x=32; y=15;
while(1) {
i=getch();
print(x, y, "+");
if(i==0x19) { if(y>0) --y; } else
if(i==0x08) { if(x>0) --x; } else
if(i==0x1A) { if(y<24) ++y; } else
if(i==0x18) { if(x<63) ++x; }
print(x, y, "O");
}
сижу рисую. :) Теперь жизнь станет проще, а программы круче.
---------- Post added at 01:36 ---------- Previous post was at 01:22 ----------
Программа
struct File {
ushort some1, size;
uchar name[11];
uchar some2;
};
File files[24];
void main() {
register uchar i;
File* f;
clrscr();
for(f=files, i=0; i<24; ++i, ++f)
itoa(f->name, i);
for(i=0; i<16; ++i)
print(0, i, files[i].name);
while(1);
}
files ds 384
main_f ds 2
main:
push bc
; 49 clrscr();
call clrscr
; 51 for(f=files, i=0; i<24; ++i, ++f)
ld hl, files
ld (main_f), hl
ld b, 0
l12:
ld a, b
cp 24
jp nc, l13
; 52 atoi(f->name, i);
ld hl, (main_f)
inc hl
inc hl
inc hl
inc hl
ld (atoi_str1), hl
ld l, b
ld h, 0
call atoi
inc b
ld hl, (main_f)
ld de, 16
add hl, de
ld (main_f), hl
jp l12
l13:
; 54 for(i=0; i<16; ++i)
ld b, 0
l15:
ld a, b
cp 16
jp nc, l16
; 55 print(0, i, files[i].name);
xor a
ld (print_x), a
ld a, b
ld (print_y), a
ld l, b
ld h, 0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld de, files
add hl, de
inc hl
inc hl
inc hl
inc hl
call print
inc b
jp l15
l16:
; 57 while(1);
l18:
jp l18
l19:
l11:
pop bc
ret
predatoralpha
15.09.2012, 02:06
А если просто вычесть и знак результата проверить?
MOV A,N1
MOV B,N2
SUB B
JM less Если сравнивать только положительные или только отрицательные - то да.
Например.
10-8 = 2. бит 7 нулевой. 10>8
(-1)-(-2)=255-254=1. бит 7 нулевой. -1>-2
А вот если одни с другими...
127 - (-2) = 127 - 254 = -127 = 0х81 бит 7 ненулевой. 127<-2. Но это неверно!
Тут надо не только знак учитывать, но и флаг переполнения. И если есть переполнение - реверсировать результат.
MOV A,N1
MOV B,N2
SUB B
MOV B,A
RRA
XOR B
JM less
В коде не совсем уверен, сейчас несколько нетрезв.
Возможно пригодится - сравнение 16 битных знаковых чисел
;DE и HL - знаковые 16 битные целые
;Портит A и B
CmpDEHL: mov a,d
xra h
jm DiffSigns
mov a,e
sub l
mov b,a
mov a,d
sbb h
rc ;DE<HL (CY=1)
ora b
ret ;или DE>HL (CY=0;Z=0) или DE=HL (CY=0;Z=1)
DiffSigns: xra h
rp ;DE>HL (CY=0;Z=0)
stc
ret ;DE<HL (CY=1)
Чуть измененный вариант из книги
Григорьев В.Л. Программное обеспечение микропроцессорных систем. М.: Энергоатомиздат, 1983 (с. 173-174)
Если автор топика задумал написть свой компилятор Си под ВМ80, то может ему пригодится книга "Компилятор Си для микроЭВМ" Д.Хендрикс. В ней описывается Смолл-Си.
Вот ссылки из инета.
http://net.lg.ua/~cray/compilers/jhc.djvu
http://net.lg.ua/~cray/compilers/smallc21.rar
Тут надо не только знак учитывать, но и флаг переполнения. И если есть переполнение - реверсировать результат.
Согласен. Про переполнение я уже потом подумал, когда выключил комп :)
8-битное сравнение из 16-битного варианта вроде тоже неплохо получается:
;A и E - знаковые 8 битные целые
;Кроме флагов ничего не портит
CmpAE: xra e
jm DiffSigns
xra e
cmp e
ret ;A<E (CY=1) или A>E (CY=0;Z=0) или A=E (CY=0;Z=1)
DiffSigns:
xra e
rp ;A>E (CY=0;Z=0)
stc
ret ;A<E (CY=1)
predatoralpha
15.09.2012, 14:32
Мой вариант сравнения с инверсией флага переноса неверен.
vinxru, используй свою версию с +80. Похоже, на 7-й бит нельзя опираться.
Вчера, засыпая, отправил не в тот тред. На всякий случай повторюсь:
Ускоренные умножения и деления, вдруг пригодится:
http://www.cirsovius.de/CPM/Projekte/Artikel/Mathe/MULDIV/MULDIV.html
vinxru, используй свою версию с +80.
И я того же мне ни я.
Реализовал break, continue, return, switch, default, do {} while, union, typedef, extern, sizeof. Реализовано описание внешних функций и переменных. Функции сравнения + перехода оптимизированы. Реализован #define
---
Осталось:
Оптимизировать умножение и деление на константу.
Switch - это пока просто последовательность if, т.е. без таблицы переходов
Вставки ассемблера не реализованы.
Препроцессор не реализован: #include, #ifdef, #ifndef, #endif
Инициализации статических переменных нет Например: char data[] = { 0x10, 0x20 }; FileInfo files[] = { { "abc", 1 }, { "def", 2 } };
Контроля рекурсии нет и необходимый размер стека не определяется.
Сделать постоптимизатор ассемблера, который в том числе будет переводить программу в мненоники 8080, комплировать в BIN файл.
Режима стековых переменных нет.
В структурах нельзя будет описывать многомерные массивы. В моём компиляторе двухмерный массив - это массив указателей на одномерные массивы.
int a[5][10] - это
int* a[5];
int b[50];
a[0] = b;
a[1] = b+10;
a[2] = b+20;
a[3] = b+30;
a[4] = b+40;
Еще не сделал вычитание указателя из указателя. И с преобразованием типов не все впорядке.
---------- Post added at 12:01 ---------- Previous post was at 11:57 ----------
Так же в этой документации (http://lib.ru/MAN/DEMOS210/c.txt) описано, что перед любой арифметической операцией надо 8 битные типы данных приводить к 16 битным.
в моем же случае любая операция между 8 битными значениями даст 8 битный результат. Кроме умножения, оно дает 16 бит.
А любое сравнение или операция НЕ дает 8 битный результат.
Команда XTHL пригодилась.
predatoralpha
18.09.2012, 11:52
Команда XTHL пригодилась.А на каком коде? Не хватало регистров?
На команде загрузить в HL значение с верхушки стека. Но перед тем как использовать HL, надо его прошлое значение сохранить в стек.
Что то типа
POP DE
PUSH HL
EX HL, DE
И даже команда DEC SP пригодилась. Для варианта
PUSH DE
...
INC SP
POP AF
DEC SP
predatoralpha
18.09.2012, 12:00
На команде загрузить в HL значение с верхушки стека. Но перед тем как использовать HL, надо его прошлое значение сохранить в стек.Я понимаю что делает команда. А на каком сишном коде она "всплыла"?
Ради отладки компилятора переписал с JS игру. Почти полностью на Си.
http://s019.radikal.ru/i615/1209/b9/3875185eea1d.png
Обещал в среду, поэтому выкладываю что есть. Я сегодня, внезапно, опять с нуля переписал кодогенератор. Переписать до конца не успел и некоторые вещи компилируется не оптимально.
При этом синтаксисом Си почти не занимался, там то же есть масса косяков.
Косяки все не перечисляю, поскольку их там несколько десятков. Но основное это:
1) Преобразование типов криво написано. Никакого автоматического преобразования нет. То есть signed -> unsigned, short -> char и т.п. При вычитании указателей деление на размер может не производится. Сравнение указателя с нулем не реализовано. Я просто не успел.
2) Инициализация статических данных написана в самом простом варианте и не совсем корректно. ( uchar code[4] = { 1,2,3,4 }; )
3) #define действует на #define :)
4) Переменные и функции не контролируются на дубликаты.
5) switch реализован криво. Забыл. (Два или более подряд идущих CASE быть не должно. Перед CASE должен быть BREAK, иначе поведение не определено)
И по оптимизации основное:
1) Операции && и || и ! работают крайне медленно. Хотя и корректно. Не успел дописать оптимизацию.
2) Дублирующиеся бесполезные команды ассемблера будут удаляться на следующем этапе компиляции, который я еще не дописал.
3) Размеры структур должны быть кратны степени двойки (но об этом и так вам сообщит компилятор)
4) Не все варианты команд реализованы, есть вероятность схватить ошибку типа "pokeHL 12"
P.S. Можете добавить в список, но у меня пока дел хватает и ошибки я правлю когда они сильно мешают жить.
Во вложении компилятор и исходники игрушки. Игрушка тормозит не по детски, не синхронизируется с кадровой разверткой, но вроде работает. Для цветного Апогея.
Пример кода
;--- drawSprite -----------------------------------------------------------------
drawSprite_x ds 1
drawSprite_y ds 1
drawSprite_s ds 2
drawSprite_v ds 2
drawSprite_a ds 1
drawSprite:
ld (drawSprite_s), hl
; 8 a = (uchar)(x/SPRITE_WIDTH*5);
ld a, (drawSprite_x)
rra
rra
and 3Fh
ld d, 5
call op_mul
ld a, l
ld (drawSprite_a), a
; 9 v = (uchar*)(0xA009 + 94*6 + a + y*94);
ld hl, (drawSprite_a)
ld h, 0
ld de, 41533
add hl, de
push hl
ld d, 94
ld a, (drawSprite_y)
call op_mul
pop de
add hl, de
ld (drawSprite_v), hl
; 11 switch(x % SPRITE_WIDTH) {
ld a, (drawSprite_x)
and 3
cp 0
jp nz, l24
; 12 drawImage (v, CYAN, s); break;
ld hl, (drawSprite_v)
ld (drawImage_to), hl
ld a, 129
ld (drawImage_color), a
ld hl, (drawSprite_s)
call drawImage
; 12 break;
jp l23
l24:
cp 1
jp nz, l25
; 13 drawImage1(v, CYAN, s); break;
ld hl, (drawSprite_v)
ld (drawImage1_to), hl
ld a, 129
ld (drawImage1_color), a
ld hl, (drawSprite_s)
call drawImage1
; 13 break;
jp l23
l25:
cp 2
jp nz, l26
; 14 drawImage2(v, CYAN, s); break;
ld hl, (drawSprite_v)
ld (drawImage2_to), hl
ld a, 129
ld (drawImage2_color), a
ld hl, (drawSprite_s)
call drawImage2
; 14 break;
jp l23
l26:
cp 3
jp nz, l27
; 15 drawImage3(v, CYAN, s); break;
ld hl, (drawSprite_v)
ld (drawImage3_to), hl
ld a, 129
ld (drawImage3_color), a
ld hl, (drawSprite_s)
call drawImage3
; 15 break;
jp l23
l23:
l27:
; 18 a = (uchar)((y/SPRITE_WIDTH)*MAP_WIDTH+(x/SPRITE_HEIGHT));
ld a, (drawSprite_y)
rra
rra
and 3Fh
ld h, 0
ld l, a
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld a, (drawSprite_x)
rra
rra
and 3Fh
ld e, a
ld d, 0
add hl, de
ld a, l
ld (drawSprite_a), a
; 19 map1[a]=0xFF;
ld hl, (drawSprite_a)
ld h, 0
ld de, map1
add hl, de
ld a, 255
ld (hl), a
; 20 if(x%MAP_WIDTH) {
ld a, (drawSprite_x)
and 0Fh
or a
jp z, l28
; 21 map1[a+1] = 0xFF;
ld a, (drawSprite_a)
add 1
ld h, 0
ld l, a
ld de, map1
add hl, de
ld a, 255
ld (hl), a
; 22 if(y%MAP_WIDTH) {
ld a, (drawSprite_y)
and 0Fh
or a
jp z, l29
; 23 map1[a+MAP_WIDTH]=0xFF;
ld a, (drawSprite_a)
add 16
ld h, 0
ld l, a
ld de, map1
add hl, de
ld a, 255
ld (hl), a
; 24 map1[a+(MAP_WIDTH+1)]=0xFF;
ld a, (drawSprite_a)
add 17
ld h, 0
ld l, a
ld de, map1
add hl, de
ld a, 255
ld (hl), a
l29:
jp l30
l28:
; 27 if(y%MAP_WIDTH) {
ld a, (drawSprite_y)
and 0Fh
or a
jp z, l31
; 28 map1[a+MAP_WIDTH]=0xFF;
ld a, (drawSprite_a)
add 16
ld h, 0
ld l, a
ld de, map1
add hl, de
ld a, 255
ld (hl), a
l31:
l30:
l22:
ret
;--- redrawTiles -----------------------------------------------------------------
redrawTiles_v ds 2
redrawTiles_a ds 1
redrawTiles_addr ds 1
redrawTiles:
push bc
; 39 for(m = map1; m - (map1+MAP_WIDTH*MAP_HEIGHT); ++m) {
ld bc, map1
l33:
ld hl, -((map1)+(208))
add hl, bc
ld a, l
or h
jp z, l34
; 40 if(!*m) continue;
ld a, (bc)
or a
jp nz, l37
; 40 continue;
jp l35
l37:
; 41 *m = 0;
xor a
ld (bc), a
; 43 addr = (uchar)(m - map1);
ld hl, -(map1)
add hl, bc
ld a, l
ld (redrawTiles_addr), a
; 44 a = map[addr];
ld hl, (redrawTiles_addr)
ld h, 0
ld de, map
add hl, de
ld a, (hl)
ld (redrawTiles_a), a
; 45 v = (uchar*)(0xA009 + 94*6 + (addr/MAP_WIDTH)*(94*4) + 5*(addr%MAP_WIDTH));
ld a, (redrawTiles_addr)
rra
rra
rra
rra
and 0Fh
ld de, 376
ld h, 0
ld l, a
call op_mul16
ld de, 41533
add hl, de
ld a, (redrawTiles_addr)
and 0Fh
push hl
ld d, a
ld a, 5
call op_mul
pop de
add hl, de
ld (redrawTiles_v), hl
; 46 if(a==0) {
ld a, (redrawTiles_a)
cp 0
jp nz, l39
; 47 drawEmptyImage(v);
ld hl, (redrawTiles_v)
call drawEmptyImage
jp l40
l39:
; 49 if(a >= 0x80) a=2;
ld a, (redrawTiles_a)
cp 128
jp c, l42
; 49 a=2;
ld a, 2
ld (redrawTiles_a), a
l42:
; 50 a--;
ld hl, redrawTiles_a
ld a, (hl)
dec (hl)
; 51 drawImage(v, tilesColors[a], tiles + a*16);
ld hl, (redrawTiles_v)
ld (drawImage_to), hl
ld hl, (redrawTiles_a)
ld h, 0
ld de, tilesColors
add hl, de
ld a, (hl)
ld (drawImage_color), a
ld hl, (redrawTiles_a)
ld h, 0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld de, tiles
add hl, de
call drawImage
l40:
l35:
inc bc
jp l33
l34:
l32:
pop bc
ret
;--- redrawScreen -----------------------------------------------------------------
redrawScreen_i ds 1
redrawScreen_s ds 2
redrawScreen:
push bc
; 61 redrawTiles();
call redrawTiles
; 63 s = enemySprite + enemyAnimation[(ticks/4)%4];
ld a, (ticks)
rra
rra
and 3Fh
and 3
ld h, 0
ld l, a
ld de, enemyAnimation
add hl, de
ld l, (hl)
ld h, 0
ld de, enemySprite
add hl, de
ld (redrawScreen_s), hl
; 64 for(e=enemies, i=0; i<enemyCount; ++i, ++e)
ld bc, enemies
xor a
ld (redrawScreen_i), a
l44:
ld hl, enemyCount
ld a, (redrawScreen_i)
cp (hl)
jp nc, l45
; 65 drawSprite(e->x, e->y, s);
ld a, (bc)
ld (drawSprite_x), a
ld hl, bc
inc hl
ld a, (hl)
ld (drawSprite_y), a
ld hl, (redrawScreen_s)
call drawSprite
l46:
ld hl, redrawScreen_i
inc (hl)
inc bc
inc bc
inc bc
inc bc
jp l44
l45:
; 67 if(playerDir==1 || playerDir==2) s = playerSprite + 16 + 16*(1 + (ticks / 2)&1); else
ld a, (playerDir)
cp 1
ld a, 1
jp z, l48
dec a
l48:
push af
ld a, (playerDir)
cp 2
ld a, 1
jp z, l49
dec a
l49:
ld d, a
pop af
or d
or a
jp z, l50
; 67 s = playerSprite + 16 + 16*(1 + (ticks / 2)&1); else
ld a, (ticks)
cp a
rra
add 1
and 1
ld d, a
ld a, 16
call op_mul
ld de, (playerSprite)+(16)
add hl, de
ld (redrawScreen_s), hl
jp l51
l50:
; 68 if(playerDir==3 || playerDir==4) s = playerSprite + 16*3 + 16*(1 + (ticks / 2)&1);
ld a, (playerDir)
cp 3
ld a, 1
jp z, l52
dec a
l52:
push af
ld a, (playerDir)
cp 4
ld a, 1
jp z, l53
dec a
l53:
ld d, a
pop af
or d
or a
jp z, l54
; 68 s = playerSprite + 16*3 + 16*(1 + (ticks / 2)&1);
ld a, (ticks)
cp a
rra
add 1
and 1
ld d, a
ld a, 16
call op_mul
ld de, (playerSprite)+(48)
add hl, de
ld (redrawScreen_s), hl
jp l55
l54:
; 69 s = playerSprite;
ld hl, playerSprite
ld (redrawScreen_s), hl
l55:
l51:
; 70 drawSprite(playerX, playerY, s);
ld a, (playerX)
ld (drawSprite_x), a
ld a, (playerY)
ld (drawSprite_y), a
ld hl, (redrawScreen_s)
call drawSprite
l43:
pop bc
ret
;--- getKeybFire -----------------------------------------------------------------
getKeybFire:
; 2 *(uchar*)0xED00 = ~(1<<7);
ld a, 127
ld (60672), a
; 3 return !(*(uchar*)0xED01 & 0x80);
ld a, (60673)
and 128
or a
ld a, 0
jp nz, l57
inc a
l57:
jp l56
l56:
ret
;--- getKeybArrows -----------------------------------------------------------------
getKeybArrows:
; 7 *(uchar*)0xED00 = ~(1<<1);
ld a, 253
ld (60672), a
; 8 return ~(*(uchar*)0xED01);
ld a, (60673)
cpl
jp l58
l58:
ret
lvd:
db 35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,32
db 35,32,32,32,55,32,32,50,32,32,32,32,32,32,35,32
db 35,32,35,32,35,32,35,50,35,32,35,32,35,32,35,32
db 35,32,32,32,32,54,50,50,32,32,32,50,32,32,35,32
db 35,32,35,32,35,50,35,32,35,32,35,50,35,32,35,32
db 35,52,50,54,50,50,32,32,32,54,50,50,50,32,35,32
db 35,32,35,64,35,50,35,32,35,50,35,32,35,32,35,32
db 35,32,50,32,32,50,64,32,32,32,50,32,32,32,35,32
db 35,50,35,50,35,50,35,54,35,50,35,32,35,32,35,32
db 35,32,32,32,32,50,32,32,32,50,32,64,54,32,35,32
db 35,50,35,32,35,32,35,50,35,50,35,50,35,32,35,32
db 35,32,50,32,32,32,50,64,32,32,32,32,32,32,35,32
db 35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,32
;--- printKeyb -----------------------------------------------------------------
printKeyb:
push bc
; 52 for(x=0; x<8; x++) {
ld b, 0
l60:
ld a, b
cp 8
jp nc, l61
; 53 *(uchar*)0xED00 = ~(1<<x);
ld d, b
ld a, 1
call op_shl
cpl
ld (60672), a
; 54 for(y=0; y<8; y++)
ld c, 0
l64:
ld a, c
cp 8
jp nc, l65
; 55 ((uchar*)0xA000+94*y)[x] = (*(uchar*)0xED01 >> y) & 1;
ld d, c
ld a, 94
call op_mul
ld de, 40960
add hl, de
ld e, b
ld d, 0
add hl, de
ld d, c
ld a, (60673)
call op_shr
and 1
ld (hl), a
l66:
ld a, c
inc c
jp l64
l65:
l62:
ld a, b
inc b
jp l60
l61:
l59:
pop bc
ret
moveBonus: ds 1
moveExit: ds 1
moveDead: ds 1
В следующей версии будет часть ошибок поправлена и оптимизатор-удалятор подключен. Он так же сможет формировать программу в мненониках 8080.
predatoralpha
20.09.2012, 14:37
Работа проделана огромная! Молодец! Будем следить!
Oleg N. Cher
21.09.2012, 20:49
Присоединяюсь. Браво! Даже не ожидал, что в наше время возможна разработка проекта такой сложности на чистом энтузиазме.
Сравнительно просто можно реализовать в Вашем компиляторе оптимизацию, которая встречается редко в каком компиляторе, сделанном не большой фирмой за финансирование. Способ основан на отслеживании кодогенератором состояния процессора: флагов, регистров и т.д. Имея и актуализируя после генерации каждой команды набор такой информации, легко встроить различные оптимизации: загрузки адресов, значений, опора на состояние флагов (ну раз значение точно известно) и т.д. Заведите “слово состояния процессора”, представляющее собой запись (структуру в Си-терминологии) состояния различных флагов, регистров и т.п., что будет выглядеть примерно так:
TYPE
FlagStatus = SET; (* Off, On, Unknown *)
VAR
cpuStat = RECORD
flagZ, flagC, flagM ... : FlagStatus; (* Статус флагов *)
regA, regB, regC ... : BOOLEAN; (* Есть ли в регистре точно известное значение? *)
regA_val, regB_val, regC_val ... : INTEGER; (* Если оно есть, то значение RegX_val имеет смысл *)
END;Теперь, допустим, нам по коду надо обнулить переменную var1. Делать мы это решили через засылку значения в неё из регистра A. Проверяем, а может в A уже ноль?
IF regA & (regA_val = 0) THEN
(* Можно не генерировать обнуление, т.к. в A остался ноль, сформированный предыдущим кодом... *)
ELSE
Gen("XRA A");
(* Теперь скорректируем состояние процессора, вызванное командой XRA A *)
cpuStat.flagZ := On; cpuStat.regA := TRUE; cpuStat.regA_val := 0; ...
END;
Gen("STA var1");Или надо загрузить в HL адрес переменной varB, а там загружен адрес varA, которые идут подряд (отличаются единичкой):
IF regHL & (regHL_val = varA_addr) THEN
(* Можно не генерировать установку адреса, он уже там... *)
ELSIF regHL & (regHL_val = varA_addr + 1) THEN (* Адрес в HL, но он на единицу меньше, чем надо *)
Gen("INX H");
ELSIF regHL & (regHL_val = varA_addr - 1) THEN (* Адрес в HL, но он на единицу больше, чем надо *)
Gen("DCX H");
ELSE (* Что ни пробовали, но таки надо грузить адрес в HL... *)
Gen("LXI H, varA");
END;Извиняюсь, если где перепутал мнемонику, давно для РК-86 не кодил. :)
P.S. Игру в эмуле Башкирия-2М (http://bashkiria-2m.narod.ru) запустить удалось только в ч/б. Выбираю при старте цветной Апогей, но после открытия файла он упрямо переключается на ч/б (буковка Ц пропадает из заголовка). Что я делаю не так?
Выбираю при старте цветной Апогей, но после открытия файла он упрямо переключается на ч/б (буковка Ц пропадает из заголовка).
Вы наверно через File->Open открываете, а при этом, в соответствии с EMU.ext запускается конфиг Apogee. Можно или EMU.ext изменить или грузить в конфиге цветного апогея командой I -> появится диалог, выбираете test_ok.rka и G.
Oleg N. Cher
21.09.2012, 22:31
В игре есть "баг" или "фича": если при движении вниз человечка не отпускать кнопку движения, он проходит немного вправо (если открыт путь). Влево же такого поведения не наблюдается. Заметил: редко-редко при нажатии кнопки движения человечек делает шажок не в ту сторону, но потом сам возвращается. Управлять легко, не зацепляется за края стен. Удобно. Осталось поправить мерцание спрайтов.
P.S. Игру в эмуле Башкирия-2М запустить удалось только в ч/б. Выбираю при старте цветной Апогей, но после открытия файла он упрямо переключается на ч/б (буковка Ц пропадает из заголовка). Что я делаю не так?
Да, спасибо больше. Я так и собираюсь делать. И еще много будет всяких оптимизаций. Например замена JMP, который переходит на JMP. Замена PUSH H, POP H на XCHG, XCHG. Поиск одинаковых фрагментов кода и вынесение из в функцию.
Но пока я делаю страшную вещь... Прошлая версия компилятора собирала любую команду в три этапа: загрузка первого аргумента в HL, загрузка второго аргумента в DE, работа с регистрами. Этот подход работает, но код не оптимален и сложно поддается оптимизации.
Например ushort &= register uchar*
;oSAnd pConstStrRef16 pBCRef8
ld a, (bc)
ld e, c
ld d, 0
ld hl, (name1)
ld a, l
and e
ld l, a
ld a, h
and d
ld h, a
ld (name1), hl
Я объединил эти три действия в одно и получил под тысячу вариантов команд. И сейчас сижу описываю более оптимальные варианты
ld hl, name1
ld a, (bc)
and (hl)
ld (hl), a
inc hl
ld (hl), 0
---------- Post added at 23:11 ---------- Previous post was at 22:58 ----------
Осталось поправить мерцание спрайтов.
Алгоритм отрисовки неправильный выбран. Но это потому что эта игра перенесена в JS. При перемещении спрайтов надо просто закрашивать лишнее черным не перерисовывая всю карту.
Вот это это выглядит. Код для 8 битного вычитания.
Для команд & | ^ + > < <= >= != == будет подобный вариант, но там команды будут проще, так как там слогаемые можно переставлять:
case pC, pA: w.ld_d_a().ld_a_c().alu_d(op); break;
заменится на
case pC, pA: w.alu_с(op); break;
void cmd_alu(AluOp op, bool self, bool flags) {
Stack& a = stack[stack.size()-2];
Stack& b = stack[stack.size()-1];
Place ap = simpleArg8(a.place);
Place bp = simpleArg8(b.place);
Writer& w = bc();
if(ap!=pA && bp!=pA)
useA();
switch(ap) {
case pA:
switch(bp) {
case pB: w.alu_b(op); break;
case pC: w.alu_c(op); break;
case pHL: w.alu_l(op); break;
case pConst: w.alu(op, b); break;
case pConstRef8: useHL(); w.ld_hl(b).alu_HL(op); break;
case pConstStrRefRef8: useHL(); w.ld_hl_ref(b).alu_HL(op); break;
case pHLRef8: w.alu_HL(op); break;
case pBCRef8: useHL(); w.ld_hl_bc().alu_HL(op); break;
case pStack8: w.pop_de().alu_d(op); break;
case pStack16: w.pop_de().alu_e(op); break;
case pStackRef8: chkHL(); w.pop_hl().alu_HL(op); break;
default: cmd_alu_raise(ap, bp);
}
break;
case pB:
switch(bp) {
case pA: w.ld_d_a().ld_a_b().alu_d(op); break;
case pB: w.ld_a_b().alu_b(op); break;
case pC: w.ld_a_b().alu_c(op); break;
case pHL: w.ld_a_b().alu_l(op); break;
case pConst: w.ld_a_b().alu(op, b); break;
case pConstRef8: w.ld_a_b().ld_hl(b).alu_HL(op); break;
case pConstStrRefRef8: useHL(); w.ld_a_b().ld_hl_ref(b).alu_HL(op); break;
case pHLRef8: w.ld_a_b().alu_HL(op); break;
case pBCRef8: w.ld_a_BC().ld_d_a().ld_a_b().alu_d(op); break;
default: cmd_alu_raise(ap, bp);
}
break;
case pC:
switch(bp) {
case pA: w.ld_d_a().ld_a_c().alu_d(op); break;
case pB: w.ld_a_c().alu_b(op); break;
case pC: w.ld_a_c().alu_c(op); break;
case pHL: w.ld_a_c().alu_l(op); break;
case pConst: w.ld_a_c().alu(op, b); break;
case pConstRef8: w.ld_a_c().ld_hl(b).alu_HL(op); break;
case pConstStrRefRef8: useHL(); w.ld_a_c().ld_hl_ref(b).alu_HL(op); break;
case pHLRef8: w.ld_a_c().alu_HL(op); break;
case pBCRef8: w.ld_a_BC().ld_d_a().ld_a_b().alu_d(op); break;
default: cmd_alu_raise(ap, bp);
}
break;
case pHL:
switch(bp) {
case pA: w.ld_d_a().ld_a_l().alu_d(op); break;
case pB: w.ld_a_l().alu_b(op); break;
case pC: w.ld_a_l().alu_c(op); break;
case pConst: w.ld_a_l().alu(op, b); break;
case pConstRef8: w.ld_a_l().ld_hl(b).alu_HL(op); break;
case pConstStrRefRef8: w.ld_a_l().ld_hl_ref(b).alu_HL(op); break;
case pBCRef8: w.ld_a_BC().ld_d_a().ld_a_l().alu_d(op); break;
default: cmd_alu_raise(ap, bp);
}
break;
case pConst:
switch(bp) {
case pA: w.ld_d_a().ld_a(a).alu_d(op); break;
case pB: w.ld_a(a).alu_b(op); break;
case pC: w.ld_a(a).alu_c(op); break;
case pHL: w.ld_a(a).alu_l(op); break;
case pConstRef8: if(lastHL==-1) { w.ld_hl(b).ld_a(a).alu_HL(op); break; } // 10+7+7=24
w.ld_a_ref(b).ld_d_a().ld_a(a).alu_d(op); break; // 13+5+7+7=32
case pConstStrRefRef8: useHL(); w.ld_hl_ref(b).ld_a(a).alu_HL(op); break;
case pHLRef8: w.ld_a(a).alu_HL(op); break;
case pBCRef8: w.ld_a_BC().ld_d_a().ld_a(a).alu_d(op); break; // это 7+5+7+4=23, против ld_hl_bc().ld_a(a).alu_HL(op), которым еще нужен HL
default: cmd_alu_raise(ap, bp);
}
break;
case pConstRef8:
switch(bp) {
case pA: w.ld_d_a().ld_a_ref(a).alu_d(op); break;
case pB: w.ld_a_ref(a).alu_b(op); break;
case pC: w.ld_a_ref(a).alu_c(op); break;
case pHL: w.ld_a_ref(a).alu_l(op); break;
case pConst: w.ld_a_ref(a).alu(op, b); break;
case pConstRef8: if(lastHL==-1) { w.ld_hl(b).ld_a_ref(a).alu_HL(op); break; } // 10+13+7=30
w.ld_a_ref(b).ld_d_a().ld_a_ref(a).alu_d(op); break; // 13+5+13+4=35
case pConstStrRefRef8: useHL(); w.ld_hl_ref(b).ld_a_ref(a).alu_HL(op); break; // 16+13+7=36
case pHLRef8: w.ld_a_ref(a).alu_HL(op); break;
case pBCRef8: w.ld_a_BC().ld_d_a().ld_a_ref(a).alu_d(op); break;
default: cmd_alu_raise(ap, bp);
}
break;
case pConstStrRefRef8:
switch(bp) {
case pA: useHL(); w.ld_d_a() .ld_hl_ref(a).ld_a_HL().alu_d(op); break;
case pB: useHL(); w .ld_hl_ref(a).ld_a_HL().alu_b(op); break;
case pC: useHL(); w .ld_hl_ref(a).ld_a_HL().alu_c(op); break;
case pHL: w.ld_d_l() .ld_hl_ref(a).ld_a_HL().alu_d(op); break;
case pConst: useHL(); w .ld_hl_ref(a).ld_a_HL().alu(op,b); break;
case pConstRef8: useHL(); if(self) { w.ld_hl(a) .ld_d_HL().ld_hl_ref(a).ld_a_HL().alu_d(op); break; } // 10+7+16+7+4 = 44
w.ld_hl_ref(a).ld_a_HL().ld_hl(b).alu_HL(op); break; // 16+7+10+7 = 40
case pConstStrRefRef8: useHL(); if(self) { w.ld_hl_ref(a).ld_d_HL().ld_hl_ref(a).ld_a_HL().al u_d(op); break; } // 16+7+16+7+4 = 50
w.ld_hl_ref(a).ld_a_HL().ld_hl_ref(b).alu_HL(op); break; // 16+7+16+7 = 46
case pHLRef8: w.ld_d_HL() .ld_hl_ref(a).ld_a_HL().alu_d(op); break;
case pBCRef8: useHL(); w.ld_a_BC().ld_d_a() .ld_hl_ref(a).ld_a_HL().alu_d(op); break;
default: cmd_alu_raise(ap, bp);
}
a.place = a.place==pConstStrRefRef8 ? pHLRef8 : pHLRef16; // Оптимизация
lastHL = stack.size()-2;
break;
case pHLRef8: // Если self, то HL изменять нельзя
switch(bp) {
case pA: w.ld_d_a() .ld_a_HL().alu_d(op); break;
case pB: w .ld_a_HL().alu_b(op); break;
case pC: w .ld_a_HL().alu_c(op); break;
case pConst: w .ld_a_HL().alu(op,b); break;
case pConstRef8: w.ld_a_ref(b).ld_d_a() .ld_a_HL(); break;
case pConstStrRefRef8:if(self){ w.ex_hl_de().ld_hl_ref(b).ld_h_HL().ex_hl_de().ld_ a_HL().alu_d(op); break; }
w.ld_a_HL().ld_hl_ref(b).alu_HL(op); break;
case pBCRef8: w.ld_a_BC().ld_d_a() .ld_a_HL().alu_d(op); break;
default: cmd_alu_raise(ap, bp);
}
break;
case pBCRef8:
switch(bp) {
case pA: w.ld_d_a() .ld_a_BC().alu_d(op); break;
case pB: w .ld_a_BC().alu_b(op); break;
case pC: w .ld_a_BC().alu_c(op); break;
case pHL: w .ld_a_BC().alu_l(op); break;
case pConst: w .ld_a_BC().alu(op,b); break;
case pConstRef8:if(lastHL==-1){ w.ld_hl(b) .ld_a_BC().alu_HL(op); break; } // 10+7+7 = 24
w.ld_a_ref(b).ld_d_a() .ld_a_BC().alu_d(op); break; // 13+5+7+4 = 29
case pConstStrRefRef8: useHL(); w.ld_hl_ref(b) .ld_a_BC().alu_HL(op); break;
.... Съедено сайтом ...
default: cmd_alu_raise(ap, bp);
}
if(self) {
switch(a.place) {
case pConstStrRef8:
case pConstRef8: w.ld_ref_a(a); break;
case pHLRef8: w.ld_HL_a(); break;
case pBCRef8: w.ld_BC_a(); break;
default: cmd_alu_self_raise(a.place, bp);
}
asm_pop();
if(flags) raise("flags!");
} else {
asm_pop();
asm_pop();
if(flags) stack.push_backr().place = pFlags;
else popTmpA();
}
}
predatoralpha
22.09.2012, 00:55
vinxru, тебе ещё все предстоит в очередной раз переделывать, когда будешь вести по каждой переменной её состояние, и в зависимости от этого код будет разный. Приблизительный пример.
Твой код, сейчас:
; 54 for(y=0; y<8; y++)
ld c, 0
l64:
ld a, c
cp 8
jp nc, l65
...
l66:
ld a, c
inc c
jp l64
l65:
Тут есть два варианта - если условие по старту верное (как в этом коде), то такой код неоптимален, и лучше что-то типа:
; 54 for(y=0; y<8; y++)
ld c, 0
l64:
...
ld a, c
inc c
cp 7
jp c, l64
а вот если условие неопределённое, то нужен такой код как есть.
Нужно на каждую переменную вести её последнее значение, на этапе компиляции анализировать условия (y<8 в данном случае). И в зависимости от этого анализа условия компилятор выбирает нужную ветку.
Ты молодец, много сделал, но тут ещё копать и копать. И боюсь, ещё не раз придётся всё переписать...
Замена for(a;b;c) на a; do ... c; while(b); ? Я не думал об этом, так как это может сделать программист, но сделаем.
---------- Post added at 01:15 ---------- Previous post was at 01:10 ----------
И боюсь, ещё не раз придётся всё переписать...
Слово переписывтаь не совсем корректное. Я сейчас подключаю новые блоки поверх простых конструкций. Расширяем :)
Можно было бы давно оставить в покое кодогенератор, добить синтаксис Си, добавить оптимизатор. Но хочется, что бы потом пальцем не показывали, что бы компилятор работал на уровне или даже лучше компиляторов для Z80.
---------- Post added at 01:31 ---------- Previous post was at 01:15 ----------
Нужно на каждую переменную вести её последнее значение, на этапе компиляции анализировать условия (y<8 в данном случае).
Это будет отдельный блок компилятора. Сначала программа сваливается в буфер. Я для этого удобный способ офомил: w.ld_hl_ref(b).ld_d_HL().ld_a_c();
А уже потом, несколько алгоритмов оптимизации будут сверху-вниз и снизу-верх обрабатывать программу.
---------- Post added at 03:18 ---------- Previous post was at 01:31 ----------
Добавил замену команды LD A на INC A, DEC A в зависимости от состояния A. Вам очень этого хотелось :) Еще добавлю CPL туда же и для всех регистров сделаю. Сделал нормальную поддержку операторов && || !
Надо еще расписать все варианты для 16-битной арифметики.
startGame:
push bc
; 375 enemyCount = 0;
xor a
ld (enemyCount), a
; 376 playerX = SPRITE_WIDTH;
ld a, 4
ld (playerX), a
; 377 playerY = SPRITE_HEIGHT;
;o a=4
ld (playerY), a
; 378 gameOver = 0;
xor a
ld (gameOver), a
; 379 fireSize = 2;
ld a, 2
ld (fireSize), a
; 380 fireMax = 2;
;o a=2
ld (fireMax), a
; 381 bombCount = 0;
xor a
ld (bombCount), a
; 382 prevFire = 1;
inc a ;o a=1
ld (prevFire), a
; 383 gameOver = 0;
xor a
ld (gameOver), a
; 385 memset(map1, 0xFF, MAP_WIDTH*MAP_HEIGHT);
ld hl, map1
ld (memset_dest), hl
dec a ;o a=255
ld (memset_v), a
ld hl, 208
call memset
; 386 memcpy(map, lvd, MAP_WIDTH*MAP_HEIGHT);
ld hl, map
ld (memcpy_dest), hl
ld hl, lvd
ld (memcpy_src), hl
ld hl, 208
call memcpy
; 387 memset(times, 0, MAP_WIDTH*MAP_HEIGHT);
ld hl, times
ld (memset_dest), hl
xor a
ld (memset_v), a
ld hl, 208
call memset
; 389 p = map;
В op_shr надо CP A перенести после op_shr_2.
Возможно в memcpy и memset стоит сделать две ветки - одна для счетчика >255 (как сейчас), вторая для <=255?
Сделал оба пункта, printKeyb раскомментил - бомберман не сломался.
Процедуры/функции, которые никто не "трогает", наверно лучше не компилировать и не включать в .asm?
В op_shr надо CP A перенести после op_shr_2.
Ага, я уже заметил.
Возможно в memcpy и memset стоит сделать две ветки - одна для счетчика >255 (как сейчас), вторая для <=255?
Ага. И наверное еще сделать копирование стековыми командами. Я такое недавно видел в исходниках CP/M BIOS-а Корвета. И вообще не редкость.
Сделал оба пункта, printKeyb раскомментил - бомберман не сломался.
printKeyb - это я сделал, что бы подсмотреть сканкоды клавиш.
Процедуры/функции, которые никто не "трогает", наверно лучше не компилировать и не включать в .asm?
Да, конечно. Я построю дерево вызовов и по этому дереву станет ясно, какая функция нужна, а какая нет. И по ней же я рассчитаю и размещу в общей памяти переменные функций, которые одновременно не вызываются.
Более того, надо продумать более грамотное подключение библиотек функций. Что бы не компилировать весь код. Что то типа:
uchar getch() = stdlib.c;
uchar setPixel(uchar x, uchar y, uchar c) = apogey/graph.c;
И наверное еще сделать копирование стековыми командами.
Как вариант - 37493. Можно еще для size>=2 memset с push сделать, но не уверен, стоит ли. Со стеком надо аккуратно. Для машин с прерываниями надо (наверно на условной компиляции) di до и ei после поставить (или использовать пару типовых mem.h для машин с прерываниями/без).
В качестве оффтопа - сделал тупой немигающий вариант бомбера. Просто копирую (pop shld, почти 80000 тактов :o) из бэкбуффера в ВОЗУ, хотя на апогее надо конечно менять базовый адрес отрисовки и переключать адреса ВОЗУ.
printKeyb - это я сделал, что бы подсмотреть сканкоды клавиш.
Мне понравилось, как показывает printKeyb. Про него я упомянул в связи с тем, что в бомбере вроде только в этой процедуре используется shr.
Восьмибитное умножение можно немного оптимизировать
; Умножение A на D, результат в HL. BC не трогаем
op_mul:
ex hl, de
ld l, 0
ld e, a
ld d, l
ld a, 8
op_mul1:
add hl, hl
jp nc, op_mul2
add hl, de
op_mul2:
dec a
jp nz, op_mul1
ret
Если памяти не жалко, то еще быстрее
op_mul:
ex hl, de
ld l, 0
ld e, a
ld d, l
DUP 7
add hl, hl
jp nc, $+4
add hl, de
EDUP
add hl, hl
ret nc
add hl, de
ret
Кажется такой или подобный вариант был в "Микропроцессорных средствах и системах", только номер не помню.
В прошлом посте я сначала случайно выложил mem.h без pop, потом исправился.
Начал писать оптимизатор.
1) Выбрасываются бессмысленные команды MOV; MVI; LDA; LDAX.
2) LDA, STA заменяется на MOV A, M, MOV M, A, LDAX, STAX если HL, BC, DE содержит соответствующее значение.
3) LHLD + MVI H, 0 заменяется на MOV L, R, MVI H, 0 если R содержит соответствующее значение.
4) MVI заменяется на INC, DEC в зависимости от значения регистра.
5) MVI A заменяется на XOR A, ADD A, CMA в зависимости от значения регистра A.
6) OR A уничтожается в последовательности команд типа: ANI 16; OR A; JZ
7) Два подряд идущих ANI, ORI, XRI, ADI, SBI... объединяются.
8) Конструкции JC $+4 + RET; JC $+6 + CALL; JC $+6 + JP заменяются на одну команду типа RNC, CNC, JNC.
9) Бесполезные переходы JP $+3 убираются.
Помимо этого в самом кодогенераторе
1) Условные операторы, типа X = A<B, теперь рассчитываются используя команду SBC вместо перехода: LDA 1; CP 2; SBC A
2) Из за недоработки компилятор выдавал последовательность команд XOR A, MOV M, A в некоторых случаях. Теперь всегда будет MVI M, 0.
3) Для 16 битного сравнения и вычитания, если один из аргументов константа, используется команда ADD HL, DE. Исключение составляют 4 оператора для знакового типа SHORT: <, >, <=, =>, для которых вызывается подпрограмма.
---------- Post added at 13:42 ---------- Previous post was at 12:40 ----------
В ближайшее время займусь доработкой синтаксиса Си. Какие нибудь программы Си покомпилирую. Например Трубо-Кальк из примеров к Трубо-Си. А то как в первый день я сделал наброски, так больше и не трогал.
---------- Post added at 14:14 ---------- Previous post was at 13:42 ----------
Прикинул, какие команды я не использую:
EI, DI, DAA, CMC, STC, DCX D, HLT, SPHL, PUSH D, IN, OUT, RST, LXI SP, NOP
JP, JPO, JPE, JM
CP, CPO, CPE, CM
RP, RPO, RPE, RM,
RLC, RRC, RAL
Еще некоторые комбинации регистров в MOV, ADD, ADC, SBC, OR, AND, XOR, CP не используются.
Error404
24.09.2012, 15:25
Ага. И наверное еще сделать копирование стековыми командами. Я такое недавно видел в исходниках CP/M BIOS-а Корвета.
ни в коем случае! При выполенини таких процедур придется гасить прерывания, а это суксь. Нужна скорость - гоните процессор (а лучше поменять его на Z80).
Более того, надо продумать более грамотное подключение библиотек функций. Что бы не компилировать весь код. Что то типа:
uchar getch() = stdlib.c;
uchar setPixel(uchar x, uchar y, uchar c) = apogey/graph.c;
Надо делать обычные extern объявления (чтобы компилер мог только проверить синтаксис вызываемых внешних процедур), а связыванием должен заниматься линкер (компилеру знать про то, что где лежит, не надо): ассеблерный выхлоп компилятора будет помодульно компилироваться в объектник и далее линковаться либо включаться в библиотеки. Улилиты для этого уже есть: М80/L80/LIB80. :) Кстати, понимают оба типа мненмоник - и вменяемые, и от i8080. :)
---------- Post added at 15:25 ---------- Previous post was at 15:20 ----------
3) Для 16 битного сравнения и вычитания,
Будут ли 32-битные арифметические операции ?
Надо делать обычные extern объявления (чтобы компилер мог только проверить синтаксис вызываемых внешних процедур), а связыванием должен заниматься линкер (компилеру знать про то, что где лежит, не надо): ассеблерный выхлоп компилятора будет помодульно компилироваться в объектник и далее линковаться либо включаться в библиотеки. Улилиты для этого уже есть: М80/L80/LIB80. Кстати, понимают оба типа мненмоник - и вменяемые, и от i8080.
extern уже есть. Но вроде бы компилятор SJASM не позволяет создавать OBJ файлы.
А посоветуйте компилятор ассемблера для 8080, я бы сразу его прикрутил.
---------- Post added at 14:35 ---------- Previous post was at 14:33 ----------
Будут ли 32-битные арифметические операции ?
Будут. Я большинство из них вынесу во внешние функции, только параметры будут передаваться как адреса.
Так же будут работать float, double, long double и т.д. Все операторы внешние.
---------- Post added at 14:36 ---------- Previous post was at 14:35 ----------
ни в коем случае! При выполенини таких процедур придется гасить прерывания, а это суксь.
На РК-86 нет прерываний.
---------- Post added at 14:41 ---------- Previous post was at 14:36 ----------
Цитата:
Сообщение от Error404 Посмотреть сообщение
Надо делать обычные extern объявления (чтобы компилер мог только проверить синтаксис вызываемых внешних процедур), а связыванием должен заниматься линкер (компилеру знать про то, что где лежит, не надо): ассеблерный выхлоп компилятора будет помодульно компилироваться в объектник и далее линковаться либо включаться в библиотеки. Улилиты для этого уже есть: М80/L80/LIB80. Кстати, понимают оба типа мненмоник - и вменяемые, и от i8080.
extern уже есть. Но вроде бы компилятор SJASM не позволяет создавать OBJ файлы.
А посоветуйте компилятор ассемблера для 8080, я бы сразу его прикрутил.
При использовании OBJ файлов нельзя разместить аргументы функций в общей памяти. Нельзя рассчитать дерево вызовов и вывести предупреждения при возникновении рекурсии.
А посоветуйте компилятор ассемблера для 8080, я бы сразу его прикрутил.
Есть, например, такой ассемблер TASM (http://home.comcast.net/~tasm/index.htm).
Могу ещё предложить консольный эмулятор CP/M для i8080, недавно тестировал в нём работу C80,M80,L80. Вроде всё работает.
Ещё я использовал FASM :) У него мощный препроцессор, я сделал файл с макросами, так он и i8080 компилировал, хотя и не поддерживает этот процессор.
Error404
24.09.2012, 17:19
extern уже есть. Но вроде бы компилятор SJASM не позволяет создавать OBJ файлы.
А посоветуйте компилятор ассемблера для 8080, я бы сразу его прикрутил
Если для МС-ДОС, то я пользовался таким (во вложении - MA80/MLINK) - оно совместимо с олдскульным микрософтовским M80 по входному синтаксису асссемблера. Объектники между L80 и MLINK не совместимы, но это и не особенно нужно - главное что общая идеология раздельной компиляции.
На РК-86 нет прерываний.
Значит, по-хорошему - надо сделать 2 набора либ: со стековыми функциями (ни в одном компиляторе не видел таких реализаций, к примеру, memset, хотя не думаю что авторы не знали про такие способы заполнения памяти) и с обычными циклами. Все же универсальность куда полезнее съэкономленного такта.
При использовании OBJ файлов нельзя разместить аргументы функций в общей памяти. Нельзя рассчитать дерево вызовов и вывести предупреждения при возникновении рекурсии.
С чего это? Пременную в любом ассемблере можно как явно положить в память по абсолютному адресу при помощи EQU, так и отдать на откуп линкеру на этапе сборки. В обоих случаях в исходнике обращение к ней идет по символическому имени. Или общая память это нечто отличающееся от обычной оперативной? Не понимаю.
С чего это? Пременную в любом ассемблере можно как явно положить в память по абсолютному адресу при помощи EQU, так и отдать на откуп линкеру на этапе сборки. В обоих случаях в исходнике обращение к ней идет по символическому имени. Или общая память это нечто отличающееся от обычной оперативной? Не понимаю.
Сейчас аргументы и переменные функций располагаются в общей памяти. И это не очень оптимально, так как одновременно memset и memcpy выполняться не могут. Логично разместить их аргументы и переменные в одной области памяти.
Но что бы рассчитать, какие переменные располагать вместе, надо построить дерево вызовов функций. И жестко определить адрес для каждой переменной. Возможно относительный адрес, то есть
memset_dest EQU variables_area + 100
memset_c EQU variables_area + 102
memcpy_dest EQU variables_area + 100
memcpy_src EQU variables_area + 102
То есть потребуется анализ всех исходников и переформирование OBJ файлов каждый раз. Либо свой линкер.
выход компилятора для 8080 (просто текст программы, есть пара комяков. НАпример перепутал mvi m и shld)
Error404
24.09.2012, 18:31
Сейчас аргументы и переменные функций располагаются в общей памяти. И это не очень оптимально, так как одновременно memset и memcpy выполняться не могут. Логично разместить их аргументы и переменные в одной области памяти.
Но что бы рассчитать, какие переменные располагать вместе, надо построить дерево вызовов функций. И жестко определить адрес для каждой переменной. Возможно относительный адрес, то есть
memset_dest EQU variables_area + 100
memset_c EQU variables_area + 102
memcpy_dest EQU variables_area + 100
memcpy_src EQU variables_area + 102
То есть потребуется анализ всех исходников и переформирование OBJ файлов каждый раз. Либо свой линкер.
Я правильно понимаю - весь этот гемор оттого чтобы не передавать параметры стеком (который автоматически утилизирует память увеличением вершины стека после возврата из функции), но получить похожее удобство пользования?
Ну хрен с ним, пусть переменные не будут определены одна поверх другой - баловство все это. Не надо ничего экономить в памяти переменных, экономия получится за счет компактного кода.
Я правильно понимаю - весь этот гемор оттого чтобы не передавать параметры стеком (который автоматически утилизирует память увеличением вершины стека после возврата из функции), но получить похожее удобство пользования?
Ага
---------- Post added at 19:05 ---------- Previous post was at 18:52 ----------
Я добавил оператор $. Это 8-битное умножение. Работает быстрее.
---------- Post added at 19:06 ---------- Previous post was at 19:05 ----------
Аналогично (uchar)(a*b), только быстрее и короче. Использовать не обязательно.
---------- Post added at 19:39 ---------- Previous post was at 18:06 ----------
Ух ты блин. Эмулятор b2m подхватил названия переменных из LST файла, который генерирует tasm
http://s017.radikal.ru/i443/1209/50/73899f5f3dd1.png
Ух ты блин. Эмулятор b2m подхватил названия переменных из LST файла, который генерирует tasm
Дык для tasm и делал! :)
Сделал сумматор тактов всех команд программы. И замерял результат до работы оптимизатора и после. Пока получается оптимизация 90%. (стало/было*100)
Но в оптимизаторе нет самого главного. Он не умеет работать с ветвлениями и циклами. Сегодня добавлю их.
---------- Post added at 16:07 ---------- Previous post was at 14:12 ----------
Сделал, несколько команд пропало. Оценка 89%.
Красные - это поправки (удаления)
test:
push b
; 4 c = 100;
mvi b, 100
; 5 x = (uchar*)0xA000;
lxi h, 40960
shld test_x
; 6 do { *x = 0; ++x; } while(--c);
l0:
; 6 *x = 0; ++x; } while(--c);
lhld test_x
mvi m, 0
; 6 ++x; } while(--c);
lhld test_x
inx h
shld test_x
dcr b
mov a, b
ora a
jnz l0
l1:
pop b
ret
Версии компилятора буду выкладывать на LJ, меня сейчас тут забанят.
А посоветуйте компилятор ассемблера для 8080, я бы сразу его прикрутил.
Пока ничего удобнее http://john.ccac.rwth-aachen.de:8000/as/ не встречал. Продвинутая макро-поддержка, работает на Windows, Mac и Linux.
---------- Post added at 23:06 ---------- Previous post was at 21:23 ----------
Версии компилятора буду выкладывать на LJ, меня сейчас тут забанят.
А почему бы не создать проект на github/bitbucket/googlecode/etc?
vinxru, собрал вчера твой компилятор clang-ом под OS X. Пришлось помучиться из-за винапи и прочих премудростей VC++, которые собственно к компилятору отношения не имеют. У тебя есть интерес развивать его дальше, или делать его более портабельным?
Вроде работает, хотя с оценкой "пре-пре-пре альфа" я вынужден согласиться ;)
Если автор топика задумал написть свой компилятор Си под ВМ80, то может ему пригодится книга "Компилятор Си для микроЭВМ" Д.Хендрикс. В ней описывается Смолл-Си.
Ссылка github на него (https://github.com/ncb85/SmallC-85)
Пожалуй, выложу свое поделие на тему С-- для 8080.
Есть декомпилятор, компилятор и IDE. Изначально это делалось под архитектуру AVR, сейчас неспешно адаптирую под 8080/8085.
Представление о синтаксисе можно сложить по примеру кода под спойлером (это декомпилированная игра "тетрис" под ЮТ88).
Если очень кратко, то можно писать как на обычном асме, так и использовать си-подобные выражения. Также сейчас есть условия if и цикл loop. А ещё есть препроцессор.
var figureLanded: byte absolute 0x3659 ; 0/FF
var figureCoordinates: word absolute 0x365A
var currentRotation: byte absolute 0x365C
var figureAddress: word absolute 0x365D
var figureBuffer: byte[6] absolute 0x365F
var nextFigureData: byte[6] absolute 0x3665
var rotationPreviewBuffer: byte[6] absolute 0x366B
var remainingDelay: byte absolute 0x3671 ; Remaining delay until next game tick occurs (when space is pressed - ticks go faster)
var difficulty: byte absolute 0x3672
var randomSeed: byte absolute 0x3673
var nextFigureIndex: byte absolute 0x3674 ; 1 based
var currentFigureIndex: byte absolute 0x3675 ; 1 based
var figureX: byte absolute 0x3676
var figureY: byte absolute 0x3677
var completedLines: byte absolute 0x3678
var fullLineFlag: byte absolute 0x3679
var notSkipDrawing: byte absolute 0x367A ; Bypass drawing, only calculations (0x01 - draw, 0x00 - bypass)
var gameBuffer: byte[64*25] absolute 0x367F
#define CURSOR_LEFT 0x08 ; Переместить курсор на позицию левее
#define RESET_CURSOR 0x0c ; Переместить курсор в начало экрана (верхний левый угол)
#define CURSOR_RIGHT 0x18 ; Переместить курсор на позицию правее
#define CURSOR_UP 0x19 ; Переместить курсор на строку выше
#define CURSOR_DOWN 0x1a ; Переместить курсор на строку ниже
#define CLS 0x1f ; Очистить экран
#define CURSOR_TO(x, y) 0x1b, 0x59, 0x20 + (y), 0x20 + (x)
extern proc biosPrintChar(ch: C) absolute 0xF809
extern proc biosKeyboardReadChar() absolute 0xF803
extern proc biosKeyboardCheck() absolute 0xF812 ; -> A=0 - не нажата, A=FF - нажата
extern proc biosExitToMonitor() absolute 0xF800
#define GLASS_CHAR '#' ;':'
#define FIGURE_CHAR '▉';'[';'▉';'#'
#define EMPTY_CHAR '.'
#define FIELD_WIDTH 10
#define FIELD_HEIGHT 20
inline proc waitCycleBC() {
cycle:
BC--
A = B
A |= C
jnz cycle
}
$encoding("ut88")
$output("tetris-out.rkr")
$org(0x3000)
proc init() {
notSkipDrawing = A = 1
SP = 0x3F7F
}
proc startGame() {
call printChar(CLS)
call printMessage(msg_keys_info)
HL = 0x1F10
saveregs (HL) {
A = GLASS_CHAR
loop (B = FIELD_HEIGHT+1) {
call drawBlock(y: H, x: L, ch: A)
H--
}
H++
loop (B = FIELD_WIDTH+2) {
call drawBlock(y: H, x: L, ch: A)
L++
}
L--
loop (B = FIELD_HEIGHT+1) {
call drawBlock(y: H, x: L, ch: A)
H++
}
}
L++
A = EMPTY_CHAR
loop (C = FIELD_HEIGHT) {
saveregs (HL) {
loop (B = FIELD_WIDTH) {
call drawBlock(y: H, x: L, ch: A)
L++
}
}
H--
}
select_level:
call printMessage(msg_select_level)
call readKeyboard
call printChar(A)
A -= 0x30
jc select_level
cpi 8
jnc select_level
cma
A &= 0x07
difficulty = ++A
BC = 0x4000
lbl_307D:
BC--
A = B
A |= C
jnz lbl_307D
call printMessage(msg_press_any_key)
lbl_3089:
call checkAndReadKeyboard
A |= A
HL++
jm lbl_3089
H = A
A ^= L
B = mem[HL]
A ^= M
L = A
C = mem[HL]
lbl_3097:
HL += BC
C--
jp lbl_3097
randomSeed = A = H ^ L
completedLines = A = 0
figureLanded = --A
call printMessage(msg_full_lines)
jmp gameLoop
}
proc collapseLines() {
figureLanded = A = 0
remainingDelay = A = difficulty
HL = 0x0C11
B = FIELD_HEIGHT
lbl_30C1:
fullLineFlag = A = 0xFF
loop (C = FIELD_WIDTH) {
call getCell
if (A == FIGURE_CHAR) goto lbl_30D4
fullLineFlag = A = 0
lbl_30D4:
L++
}
L = 0x11
A = fullLineFlag
A |= A
jp lbl_314F
A = difficulty-1
A = ~A
A &= 0x07
A <<= 1
D = A
A <<= 1
A += D + 5
A <<= 1
D = A
A = completedLines + 1
cmp D
cz increaseDifficulty
if (A != 0x64) goto lbl_3100
A = 0
lbl_3100:
completedLines = A
C = 0
lbl_3105:
C++
A -= 0x0A
jnc lbl_3105
A += 0x3A
xchg
HL = 0x1E0B
call drawBlock(y: H, x: L, ch: A)
L--
call drawBlock(y: H, x: L, ch: ' ')
A = C + 0x2F ; '/'
call printChar(A)
xchg
saveregs (HL) {
lbl_3123:
D = H + 1
loop (C = FIELD_WIDTH) {
E = L
xchg
call getCell
xchg
call drawBlock(y: H, x: L, ch: A)
L++
}
H++
L = 0x11
A = H
if (A != 0x1F) goto lbl_3123
HL = 0x1F11
A = EMPTY_CHAR
loop (C = FIELD_WIDTH) {
call drawBlock(y: H, x: L, ch: A)
L++
}
}
H--
lbl_314F:
H++
B--
jnz lbl_30C1
figureAddress = HL = figureBuffer
xchg
HL = nextFigureData
loop (B = sizeof(nextFigureData)) {
mem[DE++] = A = mem[HL++]
}
}
proc gameLoop() {
HL = 0x3447
DE = 0x0018
call randomValue
loop (A) {
HL += DE
}
DE = nextFigureData
saveregs (DE) {
loop (B = sizeof(nextFigureData)) {
mem[DE++] = A = mem[HL++]
}
}
HL = 0x0F06
lbl_3188:
call drawBlock(y: H, x: L, ch: ' ')
call printChar(' ')
A = ++L
if (A != 0x0C) goto lbl_3188
L = 6
A = ++H
if (A != 0x11) goto lbl_3188
call drawFigure(coords: 0x1007, address: DE, ch: FIGURE_CHAR)
A = figureLanded
A |= A
jnz collapseLines
currentRotation = A
figureCoordinates = HL = 0x1F15
figureX = A
figureY = A
call checkCollisions
jc gameOver
call drawCurrentFigureAt(FIGURE_CHAR)
}
proc nextCycle() {
B = A = remainingDelay
C = 1
}
proc keyInputLoop() {
call checkAndReadKeyboard
A |= A
jm noKeyInput()
if (A == '6') goto handleSpeedUp ; Ускорение
if (A == '7') goto handleLeft ; Влево
if (A == '8') goto handleRotate ; Вращение
if (A == '9') goto handleRight ; Вправо
if (A == 0x03) goto gameOver ; Ctrl + C
if (A == 'S') goto handlePause ; Пауза
DE = 0x3671
if (A == ' ') goto handleSpace
}
proc noKeyInput() {
BC--
A = B
A |= C
jnz keyInputLoop
figureX = A
figureY = --A
call calcMove
jnc nextCycle
jmp collapseLines
}
proc gameOver() {
call printMessage(msg_game_over_repeat)
call readKeyboard
if (A == 'Y') goto startGame
if (A == 'N') goto biosExitToMonitor
nop
nop
nop
nop
nop
nop
nop
nop
}
proc handlePause() {
saveregs (HL) {
call printMessage(msg_pause)
saveregs (BC) {
BC = 0
lbl_323A:
BC--
A = B
A |= C
jnz lbl_323A
}
waitKey:
call checkAndReadKeyboard
A |= A
jm waitKey
call printMessage(msg_clear_message)
}
jmp noKeyInput()
}
proc printMessage(msg: HL) {
loop {
A = mem[HL]
A |= A
rz
C = A
HL++
call printChar
}
}
proc printChar(ch: C) {
saveregs (HL, DE, BC) {
@printCharInternal:
saveregs (PSW) {
call biosPrintChar(C)
}
}
ret
}
proc ckeckKeyboard() {
saveregs (HL, DE, BC) {
call biosKeyboardCheck
}
ret
}
proc readKeyboard() {
saveregs (HL, DE, BC) {
call biosKeyboardReadChar
}
ret
}
proc checkAndReadKeyboard() {
call ckeckKeyboard
A |= A
jnz readKeyboard
A |= 0xFF
ret
}
; Clear current figure at current coordinates
;
; The function will clear cells under figure's blocks with '.'
proc clearCurrentFigure() {
C = EMPTY_CHAR
}
; Draw current figure at current coordinates
;
; Draw the currently falling figure at its coordinates stored in 0x365a
; The function uses 0x365d currently falling figure buffer
; C - symbol to draw the figure with
proc drawCurrentFigure(ch: C) {
HL = figureCoordinates
}
; Draw current figure
;
; Draw the currently falling figure at HL logic coordinates.
; The function uses 0x365d currently falling figure buffer
; C - symbol to draw the figure with
proc drawCurrentFigureAt(ch: C) {
xchg
HL = figureAddress
xchg
}
; Draw the figure
;
; Arguments:
; HL - logic coordinates of the figure
; DE - address of the figure buffer
; C - symbol to draw the figure with
proc drawFigure(coords: HL, address: DE, ch: C) {
call drawBlock(y: H, x: L, ch: C)
saveregs (HL) {
loop (B = 3) {
L = A = mem[DE++] + L
H = A = mem[DE++] + H
call drawBlock(y: H, x: L, ch: C)
}
call printChar(RESET_CURSOR)
}
ret
}
; Generate next random value in range 1-7.
proc randomValue() {
currentFigureIndex = A = nextFigureIndex
randomSeed = A = randomSeed + 0xBB
saveregs (BC) {
B = A
A >>= 3
A ^= B
}
A &= 0x07
jz randomValue
nextFigureIndex = A
ret
}
proc increaseDifficulty() {
saveregs (PSW) {
A = difficulty - 1
cpi 1
jnc lbl_32D4
A = 1
lbl_32D4:
difficulty = A
remainingDelay = A
A--
A = ~A
A &= 0b111
A += 0x30
saveregs (HL) {
HL = 0x1F0B
call drawBlock(y: H, x: L, ch: A)
}
}
ret
}
proc calcMove() {
saveregs (BC) {
saveregs (PSW) {
notSkipDrawing = A = 0
}
saveregs (HL) {
call proc_3312
saveregs (PSW) {
notSkipDrawing = A = 1
}
jc lbl_3305
}
call proc_3312
push HL
lbl_3305:
pop HL
figureCoordinates = HL
saveregs (PSW) {
call drawCurrentFigureAt(FIGURE_CHAR)
}
}
ret
}
; Clear the figure at current location, and then check for collisions
proc proc_3312() {
call clearCurrentFigure
}
; Check the falling figure collisions
; Carry flag set if a collision is detected
proc checkCollisions() {
HL = figureCoordinates
L = A = figureX + L
H = A = figureY + H
saveregs (HL) {
call getCell
if (A != EMPTY_CHAR) goto collision
xchg
HL = figureAddress
xchg
loop (B = 3) {
L = A = mem[DE++] + L
H = A = mem[DE++] + H
call getCell
if (A != EMPTY_CHAR) goto collision
}
}
A |= A
ret
collision:
pop HL
L = A = ~figureX + 1 + L
H = A = ~figureY + 1 + H
stc
ret
}
proc handleLeft() {
A = -1
}
proc handleMotion(dx: A) {
figureX = A
figureY = A = 0
call calcMove
DE = 0x3676 ; &figureX
}
proc handleSpace() {
B = A = remainingDelay*2
C = 1
cycle:
BC--
A = B
A |= C
jnz cycle
mem[DE] = A
BC++
jmp noKeyInput()
}
proc handleRight() {
jmp handleMotion(dx: 1)
}
proc handleRotate() {
currentRotation = A = (currentRotation + 1) & 0b11
push BC
call clearCurrentFigure
HL = 0x3441 ; Base address of all figures TODO ???????
DE = 4*6 ; Single figure size (for all rotations)
loop (A = currentFigureIndex) {
HL += DE
}
DE = 6
loop (A = currentRotation + 1) {
HL += DE
}
DE = rotationPreviewBuffer
xchg
figureAddress = HL
loop (B = sizeof(rotationPreviewBuffer)) {
mem[HL++] = A = mem[DE++]
}
figureX = A = 0
figureY = A
call checkCollisions
figureAddress = HL = figureBuffer
jnc applyRotation
currentRotation = A = (currentRotation - 1) & 0b11
jmp rotationExit
}
proc applyRotation() {
DE = rotationPreviewBuffer
loop (B = sizeof(rotationPreviewBuffer)) {
mem[HL++] = A = mem[DE++]
}
}
proc rotationExit() {
call drawCurrentFigure(FIGURE_CHAR)
pop BC
jmp proc_33F0
}
proc handleSpeedUp() {
call increaseDifficulty
}
proc proc_33F0() {
jmp handleMotion(dx: 0)
}
; Блок имеет ширину в два символа. Координаты отсчитываются от нижнего левого угла
;
; The function does 2 things:
; - Updates the block on the screen
; - Stores the new value in the game buffer
; Arguments:
; H - logical Y coordinate of the block
; L - logical X coordinate of the block
; A - Character to print as a block
proc drawBlock(y: H, x: L, ch: A) {
saveregs (HL, DE, BC) {
B = A
call calculateBlockAddress
mem[HL] = B
A = notSkipDrawing
A |= A
jnz draw
A = B
}
ret
draw:
; курсор в точку (E, D)
call printChar(0x1B)
call printChar(0x59)
call printChar(D)
call printChar(E)
C = B
A = B
cpi FIGURE_CHAR
cz printChar
if (A != EMPTY_CHAR) goto skip
call printChar(' ')
C = B
skip:
cpi GLASS_CHAR
cz printChar
A = B
jmp printCharInternal
}
; Return the cell value
;
; Arguments: HL - logical coordinates of the cell
; Return: value in A
proc getCell(xy: HL) {
saveregs (HL, DE) {
call calculateBlockAddress
A = mem[HL]
}
ret
}
; Calculate address of the given block
;
; Arguments:
; H - block vertical coordinate (bottom to top)
; L - block horizontal coordinate (left to right)
;
; Result:
; D - block vertical screen coordinate (top to bottom) + 0x20 (can be passed to Move cursor sequence)
; E - block horizontal screen coordinate + 0x20 (can be passed to Move cursor sequence)
; HL - address of the block byte in the game buffer
;
; Algorithm:
; Y = 0x20 - H ; logic coordinates
; X = L
; D = 0x20 + Y ; Screen coordinates
; E = 0x20 + 2*X
; HL = Y*0x40 + X + 0x367f ; Block address in the buffer
proc calculateBlockAddress(x: L, y: H) {
saveregs (BC) {
B = A = 0x20 - H
C = A = L*2
HL = 0x2020 + BC
xchg
HL = gameBuffer
A >>= 1
C = A
B = A = B >> 2
C = A = (A & 0xC0) | C
B = A = B & 0x3F
HL += BC
}
ret
}
byte[] {
0, 0, 0
; 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0
}
;reset_cursor:
;byte[] {
; CURSOR_TO(0, 0), 0
;}
//$org(0x345F)
; Описаний фигур и их вращения.
; Каждая фигура состоит из 4х блоков. Один блок считается центром и имеет координаты (0, 0)
; Положение остальных блоков задаются в виде приращения координат от предыдущего блока.
; Записи из 6 байт (три пары приращений кооридинат) представляют собой все 4 варианта вращения.
figure_I:
byte[] {
-1, 0, 2, 0, 1, 0, ; 1x23 ; 1 ; 1x23 ; 1 ;
0, -1, 0, 2, 0, 1, ; ; x ; ; x ;
-1, 0, 2, 0, 1, 0, ; ; 2 ; ; 2 ;
0, -1, 0, 2, 0, 1 ; ; 3 ; ; 3 ;
}
figure_L:
byte[] {
-1, 0, 2, 0, 0, -1, ; 3 ; 1 ; 2x1 ; 31 ;
0, -1, 0, 2, 1, 0, ; 1x2 ; x ; 3 ; x ;
1, 0, -2,0, 0, 1, ; ; 23 ; ; 2 ;
0, 1, 0,-2, -1,0 ; ; ; ; ;
}
figure_J:
byte[] {
1, 0, -2, 0, 0, -1, ; 3 ; 23 ; ; 1 ;
0, 1, 0, -2, 1, 0, ; 2x1 ; x ; 1x2 ; x ;
-1,0, 2, 0, 0, 1, ; ; 1 ; 3 ; 32 ;
0,-1, 0, 2, -1, 0 ; ; ; ; ;
}
figure_Z:
byte[] {
1, 0, -1, -1, -1, 0, ; 32 ; 3 ; 32 ; 3 ;
0, 1, 1, -1, 0, -1, ; x1 ; x2 ; x1 ; x2 ;
1, 0, -1, -1, -1, 0, ; ; 1 ; ; 1 ;
0, 1, 1, -1, 0, -1 ; ; ; ; ;
}
figure_S:
byte[] {
-1, 0, 1, -1, 1, 0, ; 23 ; 3 ; 23 ; 3 ;
0, 1, -1, -1, 0, -1, ; 1x ; 2x ; 1x ; 2x ;
-1, 0, 1, -1, 1, 0, ; ; 1 ; ; 1 ;
0, 1, -1, -1, 0, -1 ; ; ; ; ;
}
fugure_T:
byte[] {
-1, 0, 2, 0, -1, -1, ; 3 ; 1 ; ; 2 ;
0, -1, 0, 2, 1, -1, ; 1x2 ; x3 ; 2x1 ; 3x ;
1, 0, -2, 0, 1, 1, ; ; 2 ; 3 ; 1 ;
0, 1, 0, -2, -1, 1 ; ; ; ; ;
}
figure_O:
byte[] {
0, -1, 1, 0, 0, 1, ; 12 ; 12 ; 12 ; 12 ;
0, -1, 1, 0, 0, 1, ; x3 ; x3 ; x3 ; x3 ;
0, -1, 1, 0, 0, 1, ; ; ; ; ;
0, -1, 1, 0, 0, 1 ; ; ; ; ;
}
msg_keys_info:
byte[] {
;0x1B, 0x59, 0x24, 0x20,
CURSOR_TO(0, 4),
0x0E, "..7.. ..8.. ..9..", 0x0D, 0x0A,
"ВЛЕВО ВРАЩАТЬ ВПРАВО", 0x0D, 0x0A, 0x0A,
0x75, 0x77, 0x65,
"УВЕЛИЧИТЬ СКОРОСТЬ ..6..", 0x0D, 0x0A, 0x0A,
" ..PROBEL.. СБРОСИТЬ", 0x0D, 0x0A, 0x0F, 0x0A,
" ..S.. ", 0x0E, "ПАУЗА", 0
}
msg_select_level:
byte[] {
CURSOR_TO(0, 1),
"ВАШ УРОВЕНЬ(0-7) ? ", CURSOR_LEFT, 0
}
msg_press_any_key:
byte[] {
CURSOR_TO(0, 2),
"НАЖМИТЕ ЛЮБУЮ КЛАВИШУ !", 0
}
msg_full_lines:
byte[] {
CURSOR_TO(0, 2),
"ЧИСЛО ПОЛНЫХ СТРОК 00", 0x0D, 0
}
msg_game_over_repeat:
byte[] {
CURSOR_TO(8, 0x16),
"ИГРА ЗАКОНЧЕНА, ЖЕЛАЕТЕ ПОВТОРИТЬ?", 0x0F, " (Y/N)", 0
}
msg_pause:
byte[] {
CURSOR_TO(8, 0x16),
"ВОДИТЕЛЬ, УСТАЛ - ОТДОХНИ !", 0
}
msg_clear_message:
byte[] {
CURSOR_TO(8, 0x16),
" ", 0
}
byte[] {
0, 0, 0, 0, 0, 0
}
Компилятор написан на Котлине, последнюю сборку всегда можно скачать тут https://trolsoft.ru/content/soft/trolcodestudio/the-rat.jar.
Декомпиляция файла game.rkr делается командой
java -cp the-rat.jar ru.trolsoft.therat.i8085.decompiler.DecompilerKt game.rkr
У декомпилятора есть разные опции, в т.ч. для задачи дополнительных точек входа.
Компиляция файла source.8080 делается командой
java -jar the-rat.jar source.8080
IDE позволяет создавать и компилировать проекты (8080, AVR и С/С++). После компиляции С-- можно перемещаться по исходнику и видеть ассемблерный результат.
В целом всё это пока сыровато но вполне работоспособно.
Если сиё поделие вдруг будет кому-то интересно, постараюсь сделать более подробное описание.
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot