Покажите пальцем, где можно скачать хороший компилятор Си для процессора 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
ой, кросс не заметил ;)
можно в cp/m эмуляторе пускать
правда думаю код там ....
Есть у меня страшная мысль...
vinxru, можешь попробовать BDS C в браузере =)
http://sensi.org/~svo/i8080/c
Все-таки на 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
Надо писать си, где все переменные глобальные, если нет рекурсии.
Гений, который проектировал 8080, забыл сделать все нужные для человеческой жизни виды адресаций, зато не забыл XTHL. По-моему XTHL предположительно должен как-то помогать извлечению барахла из стека, но я так и не смог сообразить, как.
vinxru, не бросай. Если бы был компилятор Си для 8080, который бы без эмуляторных оберток работал на современной системе, это было бы уже очень хорошо. Даже просто как обертка для ассемблерных вставок он бы уже был полезен. Только структуры поддержи ;)
vinxru, прочитал название темы и аж сердце екнуло.А оказывается все только начинается.
Надо сначала ром диск закончить, а потом будем делать компилятор. Тем более, что меня далеко не раз просили. Например фанаты компьютера Львов ПК-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
Я имел ввиду не прибавлять к sp, а модифицировать только HL - ADD HL, nnn. Первый раз надо сделать так, как есть. Потом - просто менять HL на некоторое значение.
Вместо двух lxi h, 6 dad sp остаётся один add
ПС. Извиняюсь, пересмотрел систему команд, прибавления к регистровой паре значения нету. Вопрос снят.
Блин. Есть у меня проблема с постепенным расширением ТЗ.
В общем эта программа
Код: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
А как обстоят дела с макросами и #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, нет
Ой, есть :)
---------- Post added at 11:18 ---------- Previous post was at 11:15 ----------
Парсер их поддерживает, надо только пару строк написать.
---------- Post added at 11:23 ---------- Previous post was at 11:18 ----------
Да, у меня там DO <команда> WHILE(<вражение>)
Можно так писать: do a++, b++, c++; while(c<10);
Вчера во время отладки, перебирая все варианты, написал:
while(a==1) {
a=2;
} while(a==2);
И долго пялился. :)
А точно нет? В Z80 всем этим командам соответствует опкодЦитата:
Но все таки не понятно, почему команды
LD BC, IMM16
LD HL, IMM16
есть, а
LD DE, нет
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)
Тут разве что интеллектуально делать первым не HL, а DE. И команды сложения производить с ним, накапливая в нём результат. Тогда можно обойтись без обмена.
ПС. Хотя нет, арифметика работает только с HL. Блин, уже основательно подзабыл ассемблер Z80...
Да, удобная вещь. Я работал на DSP, там есть команды индексной загрузки с пост- и пред- инкрементом/декрементом, очень быстро работает перебор.
Переписал арифметику с нуля. Вместо временных переменных используется стек.
Основной код пока выглядит еще не очень. Нужно сделать хранение заначений в регистре BC и доделать сравнение (команда CP вместо вызова call e_uchar). И for парсить нелинейно, что бы код был линейным :)Код:; 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
Код: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 Кб
Как-то совсем непродуманно выглядит передача параметров в функцию.
Всмысле?
Все переменные глобальные, в том числе и аргументы функций.
Только я сделал небольшую оптимизацию, что последний параметр записывается не в память, а в регистры A или HL. А первой командой в вызываемой функции параметр записывается в память.
---------- Post added at 10:22 ---------- Previous post was at 10:17 ----------
Сделал хранение переменных в регистрах BC, B, C. Ключевое слово register. Операцию >>.
Считаю было бы стратегически правильным сразу делать шедевр.
Т.е. фукнциональность (набор операторов) - бог с ней, потом допишется, кодогенератор аналогично пока можно не оптимизировать. А вот базовые механизмы надо бы внедрить изначально. Позже это делать будет сложнее и приведет к размножению костылей и как следствие багов. Соответственно надо запланировать все нужные типы, арифметику, передачу параметров на стеке (от которых и до локальных переменных функций - полшага).
Если делать через стек, то программа будет работать еще в 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:
Несложно доделать, чтобы результат был 32-битным в DEHL:Код:_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
Код:_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 ----------
Беззнаковое деление/остаток:
DE делится на HL, результат в HL, остаток в DEКод:_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
---------- 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:
Согласен. Я просто не проверял этот вариант, упустил из виду, что 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