Quantcast
Channel: vinxru
Viewing all 319 articles
Browse latest View live

JIT + SQL

$
0
0
За прошлую неделю мне не один человек рассказал про MemSQL. Это новая база данных, которая работает исключительно в оперативной памяти. И со слов разработчиковмаркетологов это ноу-хау которое позволяет повысить производительность на несколько порядков. Маркетологи молодцы, все в курсе.

Да, здравый смысл в этом есть. Сейчас 16 Гб оперативной памяти может поставить любой, а этого объема хватит большинству баз данных. Причем, надо понимать, что файлы баз данных на диске занимают в 10-100 раз больше места, чем полезные данные. Если мы будем данные хранить в ОЗУ, то места потребуется меньше.

Почему? Диск позволяет "быстро"читать данные только блоками, притом расположенными подряд. И пытаясь выжать максимум скорости, объем приносится в жертву. И никто его не жалеет. А ОЗУ работает одинаково быстро во всем объеме. Такие жертвы не нужны и даже вредны. ОЗУ все таки мало.

А ненадежность хранения в ОЗУ решается тем, что данные в MemSQL так же записываются на диск :)

Это всё лишь маркетинг. То что они говорят о работе в памяти как о главном факторе - это маркетинг. Потому что в памяти работают многие БД. Из самых популярных: MySQL умеет создавать таблицы в ОЗУ, SQLite то же умеет всю базу держать в ОЗУ (:memory:). SQL-подобная технология LINQ в C# то же работает с данными в памяти.

Более того, печальный факт. Как только размер базы у обычной СУБД превышает размер ОЗУ, производительность начинает сильно падать. Так что уже давно оперативная память наращивается под размер БД.

Что же действительно позволяет обогнать конкурентов, так это грамотное программирование. Видимо парни пораскинули мозгами и написали более оптимальный код. И там есть куда оптимизировать. Многие вещи SQL-сервера делают очень медленно. Например вставку данных в таблицу. Разархивация БД (выполненная на основе INSERT INTO) размером в 50 Мб может длиться несколько часов.

Вот и я подумал, пора бы то же сделать свой маленький быстрый SQL с преферансом и дамами.

JIT



Делаю я его по своей инициативе по выходым, так как это потребует много времени. Неделю или две. А больше двух дней на задачу на работе мне не дают. Но даже если я не сделаю SQL, собственный JIT компилятор мне очень даже пригодится.

До этого момента я писал все компиляторы в байт-код. Интерпретатор байт-кода можно написать на чистом Си, что позволяет компилировать его под любую архитектуру, избежать множества ошибок и легально работать с любым антивирусом. Антивирусы не очень хорошо относятся к модификации исполняемого кода. На идею байт-кода меня натолкнули исходные коды эмулятора процессора Z80 в далеком прошлом :) Я когда то писал свой эмулятор Спектрума, подглядывая в чужие эмуляторы.

Причем, мой интерпретатор байт кода работал не медленнее PHP (язык программирования). И на этом я успокоился. Сейчас же нужна максимальная скорость, поэтому я перейду на уровень машинного кода. И первым моим экспериментом была простейшая программа

// Выделяем память из которой можно выполнять программы. Это основной момент
auto code = (unsigned char*)VirtualAlloc(0, 256, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
VirtualLock(code, 256);
// Записываем программу
code[0] = 0xC3; // код команды RET 
// Выполняем
((void(*)())code)();


И она запустилась :) Далее начинаем писать грамотно, точнее пишем компилятор ассемблера. Это пока для 32-х битной версии, а для 64-х битной придется вносить небольшие изменения.

// Все условия перехода
enum Cc {
  JA=7, JAE=3, JB=2, JBE=6, JC=2, JE=4, JG=0xF, JGE=0xD, JL=0xC, JLE=0xE, JNA=6, 
  JNAE=2, JNB=3, JNBE=7, JNC=3, JNE=5, JNG=0xE, JNGE=0xC, JNL=0xD, JNLE=0xF, JNO=1,
  JNP=0xB, JNS=9, JNZ=5, JO=0, JP=0xA, JPE=0xA, JPO=0xB, JS=8, JZ=4 
};

// Используемые регистры
enum Reg { EAX=0, ECX=1, EDX=2, EBX=3 };

// Команды ALU
enum Alu { ADD=0, OR=1, ADC=2, SBB=3, AND=4, SUB=5, XOR=6, CMP=7 };

class Compiler {
protected:
  unsigned char *codeStart; // Начало кода
  unsigned char *code; // Конец кода

  unsigned char *prev; int cmd, cmdA, cmdB; // Для оптимизации
  enum { CMD_mov_reg_imm=1, CMD_mov_reg_EBX_rel, CMD_mov_reg_reg, CMD_SET_A, CMD_set };// Для оптимизации

  void s(char opcode) { cmd = 0; prev = code; *code++ = opcode; }
  void s(char opcode1, char opcode2) { s(opcode1); *code++ = opcode2; }
  void i(int imm) { *(int*)code = imm; code+=4; }

public:
  Compiler() {
    prev = code = codeStart = (unsigned char*)VirtualAlloc(0, 256, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    VirtualLock(code, 256);
  }

  void execute() {
    ((void(*)())codeStart)();
  }

  // Команды без оптимизации

  void mov_reg_reg(Reg reg1, Reg reg2)   { s(0x8B, 0xC0|(reg1<<3)|reg2); cmd=CMD_mov_reg_reg; cmdA=reg1; cmdB=reg2; }
  void mov_reg_imm(Reg reg, int imm)     { s(0xB8|reg); i(imm); cmd=CMD_mov_reg_imm; cmdA=reg; cmdB=imm; }
  void mov_reg_EBX_rel(int reg, int rel) { s(0x8B, 0x83|(reg<<3)); i(rel); cmd=CMD_mov_reg_EBX_rel; cmdA=reg; cmdB=rel; }
  void mov_EBX_rel_reg(int rel, Reg reg) { s(0x89, 0x83|(reg<<3)); i(rel); }
  void push_reg(Reg reg)                 { s(0x50|reg); }
  void pop_reg(Reg reg)                  { s(0x58|reg); }
  void jcc(Cc cc, int label)             { s(0x0F, 0x80|cc); fixup(code, label); i(-4); }
  void jmp(int label)                    { s(0xE9); fixup(code, label); i(-4); }
  void ret()                             { s(0xC3); }
  void set(Cc cc, Reg reg)               { auto p=code; mov_reg_imm(reg,0); s(0x0F, 0x90|cc); *code++ = 0xC0|reg; prev=p; cmd=CMD_set; cmdA=cc; } 

  // Команды с оптимизацией

  void needFlags_jcc(Cc cc, int label)   {
    if(cc==JZ && cmd==CMD_set) { code = prev; jcc((Cc)cmdA, label); return; }
    alu_reg_reg(OR, EAX, EAX); 
    jcc(cc, label);
  }
  
  void alu_reg_imm(Alu alu, Reg reg, int imm) { 
    if(reg==EAX) { /* alu eax, imm */  s(0x05+(alu<<3)); i(imm); return; }
    /* alu reg1, imm */ s(0x81, 0xC0+(alu<<3)+reg); i(imm);
  }

  void alu_reg_reg(Alu alu, Reg reg1, Reg reg2) { 
    /* alu reg1, imm */      if(cmd==CMD_mov_reg_imm     && cmdA==reg2 ) { code=prev; alu_reg_imm(alu, reg1, cmdB); return; }
    /* alu reg1, [ebx+rel] */if(cmd==CMD_mov_reg_EBX_rel && cmdA==reg2 ) { code=prev; s(0x03+(alu<<3),  0x83+(reg1<<3)); i(cmdB); return; }
    s(0x03|(alu<<3)); *code++ = 0xC0|(reg1<<3)|reg2;
  } 

  void idiv_reg(Reg reg) { 
    /* idiv [ebx+rel] */ if(cmd == CMD_mov_reg_EBX_rel) { code = prev; s(0xF7, 0xBB); i(cmdA); return; }
    /* idiv reg */ s(0xF7, 0xF8|reg); 
  }

  void imul_reg(Reg reg) {
    /* imul [ebx+rel] */ if(cmd==CMD_mov_reg_EBX_rel && cmdA==reg) { code = prev; s(0xF7); *code++ = 0xAB; i(cmdB); return; }
    /* imul reg */ s(0xF7); *code++ = 0xE8|reg;
  }

  // Метки

  struct Fixup {
    unsigned char* code;
    int label;
    Fixup() {};
    Fixup(unsigned char* _code, int _label) { code=_code; label=_label; }
  };
  std::vector<Fixup> fixups;
  std::vector<unsigned char*> labels;

  void fixup(unsigned char* code, int label) { fixups.push_back(Fixup(code, label)); }
  void label(int label) { labels[label] = code; }
  int allocLabel() { labels.push_back(code); return labels.size()-1; }  
  void prepare() { for(auto f=fixups.begin(), fe=fixups.end(); f!=fe; f++) *(int*)f->code += labels[f->label] - f->code; }
};


Ради облегчения себе жизни в будущем я буду использовать самые простые варианты инструкций. Например команду умножения работающую только с регистрами. А уже компилятор ассемблера (код выше) будет объединять пару простых инструкций в более сложные. Например: MOV EDX, [EBX+?] + MUL EDX => MUL [EBX+?].

И во вторых, этот объект занимается компиляцией меток и переходов. Во время формирования кода, команд перехода, мы не знаем адрес перехода, если метка расположена дальше по коду. Для этого в команде перехода мы вызываем метод fixup(allocLabel()), он запоминает меcто куда надо будет записать адрес. А когда настает очередь метки, мы вызываем label, она запоминает адрес метки. А в конце вызывается метод prepare. Который расставляет все адреса.

Ну и ради теста пишем программу:

struct Item {
  int a, b;
};

std::vector<Item> data;

#define OFFSET(X) ((char*)&Item.X-(char*)&Item.a)

void do() {
  Compiler c;
  c.mov_reg_imm(EBX, (int)data.begin()._Ptr);
  int l0 = c.allocLabel();
  c.mov_reg_EBX_rel(EAX, OFFSET(a));
  c.alu_reg_imm(CMP, EAX, 16);
  int l = c.allocLabel();
  c.jcc(JA, l);
  c.mov_EBX_rel_imm(OFFSET(b), 1);
  c.label(l);
  c.alu_reg_imm(ADD, EBX, sizeof(item));
  c.alu_reg_imm(CMP, EBX, (int)data.end()._Ptr);
  c.jcc(JNZ, l0);
  c.ret();

  c.prepare();
  c.execute();
}


Эта программа выполняет действие "if(a > 16) b = 1". Выполняется быстро, но пока это совсем не похоже на SQL.

Теперь нужен компилятор c SQL. Пока что компилятор простейшего выражения. Причем, компилятор мог быть маленьким. Но я ради оптимизации решил разделить его на две части. На разбор кода и компиляцию кода собственно.

Заброшенный компилятор Си для 8080 был написан одним куском, что насколько усложнило код, что я год к нему не хотел возращаться. С новой архитектурой всё проще и опять захотелось :)

enum Type { tAcc, tVar, tImm, tStack };

struct Var {
  Type type;
  int n;

  void set(Type _type, int _n) { type=_type; n=_n; }
  ~Var();
};

class Node {
public:
  Var v; // Переменная или константа
  Operator o; // Оператор
  Cc cc; // Условие перехода
  Node *a, *b; // Аргументы оператора. Надо заменить на list args

  Node(Var& _v) { o=oNone; a=0; b=0; v=_v; }
  Node(int& n) { o=oNone; a=0; b=0; v.n=n; v.type=tImm; }
  Node(Operator _o, Cc _cc, Node* _a, Node* _b) { o=_o; cc=_cc; a=_a; b=_b; }
};

Node* parseVar(int level=0) {
  Node* a;
  if(p.ifToken("(")) { a = parseVar(0); p.needToken(")"); } else
  if(p.ifToken(ttInteger)) { a = new Node(p.i); } else
  if(p.ifToken(varNames)) { a = new Node(vars[p.i]); } else p.syntaxError();
  while(true) {
    Operator o; int l; Cc cc=JZ;
    if(level<1 && p.ifToken("Or")) l=1, o=oOr; else
    if(level<2 && p.ifToken("And")) l=2, o=oAnd; else
    if(level<3 && p.ifToken("<" )) l=3, o=oCmp, cc=JB;  else
    if(level<3 && p.ifToken("<=")) l=3, o=oCmp, cc=JBE; else
    if(level<3 && p.ifToken(">" )) l=3, o=oCmp, cc=JA;  else
    if(level<3 && p.ifToken(">=")) l=3, o=oCmp, cc=JAE; else
    if(level<3 && p.ifToken("==")) l=3, o=oCmp, cc=JE;  else
    if(level<3 && p.ifToken("!=")) l=3, o=oCmp, cc=JNE; else
    if(level<4 && p.ifToken("+" )) l=4, o=oAdd; else
    if(level<4 && p.ifToken("-" )) l=4, o=oSub; else
    if(p.ifToken("*")) l=5, o=oMul; else
    if(p.ifToken("/")) l=5, o=oDiv; else return a;
    Node* b = parseVar(l);
    a = new Node(o, cc, a, b);
  }
}


Эта функция строит дерево выражения. Некий аналог Expression Trees в .NET или еще его можно представить как программу на LISP. (это Abstract syntax tree (с) ex0_panet) Помимо разделения кода на две части, это древеро очень удобно оптимизировать. Но этот момент я пока опущу. Компилятор получился не очень большим:

void compileVar(Var* x, Node* n, int jmpIfFalse=-1) {
  // Это переменная или константа
  if(n->o==oNone) {
    *x = n->v;
    return;
  }

  // AND и OR обрабатываем по особому.
  if(n->o==oAnd || n->o==oOr) {
    if(x) jmpIfFalse = c.allocLabel();
    if(n->o==oOr) {
      int jmpIfTrue = c.allocLabel();
      int elseLabel = c.allocLabel();
      compileVar(0, n->a, elseLabel);
      c.jmp(jmpIfTrue);
      c.label(elseLabel);
      compileVar(0, n->b, jmpIfFalse);
      c.label(jmpIfTrue);
    } else {
      compileVar(0, n->a, jmpIfFalse);
      compileVar(0, n->b, jmpIfFalse);
    }
    if(x) {
      c.mov_reg_imm(EAX, 1);
      int l = c.allocLabel();
      c.jmp(l);
      c.label(jmpIfFalse);
      c.mov_reg_imm(EAX, 0);
      c.label(l);
      accUsed = x;
      x->type = tAcc;
    }
    return;
  }

  // Получаем аргументы оператора
  Var a; compileVar(&a, n->a);
  Var b; compileVar(&b, n->b);

  // Помещаем арументы в EAX, EDX
  if(b.type==tAcc) push(EDX, b);
  push(EAX, a);
  if(b.type!=tAcc) push(EDX, b);

  // Выполняем оператор
  switch(n->o) {
    case oCmp: c.alu_reg_reg(CMP, EAX, EDX); /* Требуется значение */ if(x) c.set(n->cc, EAX); break;
    case oAdd: c.alu_reg_reg(ADD, EAX, EDX); break;
    case oSub: c.alu_reg_reg(SUB, EAX, EDX); break;
    case oDiv: c.idiv_reg(EDX); break;
    case oMul: c.imul_reg(EDX); break;
    default: FatalAppExitA(1, ":)");
  }

  if(x) {
    // Требуется значение
    accUsed = x;
    x->type = tAcc;
  } else {
    // Требуется условие
    c.jcc(n->cc, jmpIfFalse);
  }
}

// Поместить значение в регистр
void push(Reg reg, Var& x, bool freeAcc=false) {
  switch(x.type) {
    case tVar: c.mov_reg_EBX_rel(reg, x.n); break;
    case tImm: c.mov_reg_imm(reg, x.n); break;
    case tAcc: if(reg!=EAX) c.mov_reg_reg(reg, EAX); break;
    case tStack: c.pop_reg(reg); break;
  }
  x.type = tAcc;
  if(freeAcc) accUsed=0;
}


И его проверка. Здесь мы компилируем выражение (a > b OR a > 18) AND b > c и его результат записываем в Item::result.

  // Начало цикла
  // for(EBX=data.begin(); EBX!=data.end(); EBX++) {

  c.mov_reg_imm(EBX, (int)data.begin()._Ptr);
  int l0 = c.allocLabel();

  // Компиляция выражения
  p.load("(a > b OR a > 18) AND b > c");
  Var out;
  compileVar(&out, parseVar());
  p.needToken(ttEof);

  // Сохраняем выражение в EBX->resut
  push(EAX, out, true);
  c.mov_EBX_rel_reg(OFFSET(result), EAX);

  // Конец цикла for(EBX=data.begin(); EBX!=data.end(); EBX++)
  c.alu_reg_imm(ADD, EBX, sizeof(item));
  c.alu_reg_imm(CMP, EBX, (int)data.end()._Ptr);
  c.jcc(JNZ, l0);
  c.ret();

  // Запуск
  c.prepare();
  c.execute();


Добавить несколько простых строк и этот код уже сможет выполнять запрос типа: SELECT выражение, выражение, выражение FROM таблица WHERE выражение.

Но надо еще сделать поддержку типов данных std::string, double, функций. Это не сложно.

Чуть сложнее будет с компиялцией слов GROUP BY, HAVING. Но я это уже делал, только транслировал не в машинный код в программу на Паскале.

SIMD



Что действительно заставляет задуматься, дак это применение SIMD инструкций (MMX/SSE/AVX/AVX-512). Это инструкции выполняющие одинаковую операцию для несколькими числами одновременно (за один такт). SSE инструкция обрабатывает за такт 128 бит данных, то есть 4 целых 32-х битных числа или 2 дробных 64-битных. Более поздние наборы инструкций AVX или AVX-512 позволяют обрабатывать 256 или 512 бит за раз.

В SQL-запросах одинаковые выражения применяются к миллионам разных строк, поэтому эти инструкции напрашиваются сами собой. Но для них необходимо данные распологать в определенном порядке. Сначала идут значения одного слобика таблицы, затем второго, затем третьего. Это надо, что бы процессор смог одним запросом вытащить из памяти значения сразу нескольких ячеек.

Как то мне один весьма умный человек, преподаватель по программирования в университете, сказал: SIMD тебе не нужен, даже обычные инструкции обрабатывают данные быстрее, чем работает ОЗУ. Прироста скорости не будет...

Lambda и Prolog

$
0
0
В прошлом я написал несколько компиляторов Пролога. И очень хотелось написать транслятор Пролога на Си, но это было невозможно, потому что в Прологе программа выполняется весьма необычным способом. Вызов функции может привести к многократному вызову функций по коду ниже. (Правильнее говорить предикат, а не функция). Например, в самом простом случае.

Совершеннолетние_клиенты(Имя) :-
  Клиенты(Имя, Возраст),
  Возраст > 18;


Первая строка выполнит многократный вызов второй строки. (Это зависит от компилятора, но предположим самый глупый компилятор). И на Си написать аналог этого примера было сложно. Конечно возможно, но машинный код был понятнее.

Вообще, в Прологе порядок строк может быть любой и на результат это не влияет. Каждая строка порождает множество вариантов и все они объединяются по И. Программа выше звучит как "Все что является клиентом"и "Всё что больше 18 лет".

Лямбда функции, появившиеся недавно в C++, позволяют красиво, то есть без хаков, представить на С++ программу на Прологе. Возьмем пример из Википеди (мне лень искать что то более интересное). Программа поиска общего делителя для массива чисел:

% Верно, что НОД (A, 0) = A
gcd2(A, 0, A).
 
% Верно, что НОД(A, B) = G, когда A>0, B>0 и НОД(B, A % B) = G (% - остаток от деления)
gcd2(A, B, G) :- A>0, B>0, N is mod(A, B), gcd2(B, N, G).
 
gcdn(A, [], A).
gcdn(A, [B|Bs], G) :- gcd2(A, B, N), gcdn(N, Bs, G).
gcdn([A|As], G) :- gcdn(A, As, G).


На C++ с Лямбдами это будет выглядеть так:

void gcd2(int a, int b, std::function<void(int)> result) {
  // Верно, что НОД (A, 0) = A
  if(b==0) result(a);
  //Верно, что НОД(A, B) = G, когда A>0, B>0 и НОД(B, A % B) = G (% - остаток от деления)
  if(a>0 && b>0) gcd2(b, a % b, result);
}
 
void gcdn(int a, int* bb, int* be, std::function<void(int)> result) {
  if(bb==be) result(a);
        else gcd2(a, *bb, [&](int n) { gcdn(n, bb+1, be, result); });
}

void gcdn(int* xb, int* xe, std::function<void(int)> result) {
  gcdn(*xb, xb+1, xe, result);
}

void test() {
  vector<int> numbers;
  numbers.push_back(36);
  numbers.push_back(6);
  numbers.push_back(10);  
  gcdn(numbers.begin(), numbers.end(), [&](int n) { 
    cout << n;
  });
}


В принципе, можно компилировать с Пролога на Си красиво

А почему бы сразу не писать так? Компилятор пролога строит план выполнения, переставляет строки, объединяет предикаты, выбрасывает лишнее. А еще определяет, какая переменная будет входящей, а какая исходящей. В предикате A=B*C, входящей может быть любая переменная и даже все сразу. Вот за этим он и нужен.

А пригодится всё это для построения оптимизаторов.

Лямбда в QT

$
0
0
Я почти уверен, что уже есть оконная библиотека для C++ построенная на лямбдах. А если нет, то её уже пишут. Это же шикарнейшая идея. Даже сборщик мусора можно красиво прикрутить.

Собственно, я прикрутил лямбды к QT.

class LambdaToSlot : public QObject {
Q_OBJECT
public:
	std::function<void()> lambda;
	LambdaToSlot(QObject* owner, std::function<void()> _lambda) : QObject(owner), lambda(_lambda) {}
	~LambdaToSlot() {}
public slots:
	void slot() { lambda(); }
};


И конечно пример использования. Динамическое добавление элементов в меню. Конечно же можно сделать свои классы под каждую задачу, но этот вариант короче и весь код одном месте.

subMenu->addAction("Открыть "+tableName, new LambdaToSlot(subMenu, [tableName]{
	openList(tableName);
}), SLOT(slot()));


А библиотека на лямбдах выглядела бы так

subMenu->addAction("Открыть "+tableName, [tableName]{
	openList(tableName);
});

Историк

$
0
0
В моей ленте были воспоминания о школе... а чем я хуже, будем вспоминать.

В нашей школе прямо на уроках все кидали мусор на пол, щелкали семечки на уроках, харкали. И историку это крайне не нравилось. Мне тогда казалось, что историк это единственный учитель школы не сидящий на транквилизаторах. Остальные учителя каждую перемену принимали успокаивающие средства типа валерьянки.

Обычно в наказание класса он запрещал проветривать помещение на перемене и все его нелюбимые классы начинали урок во влажном, плохо пахнущем классе. Мне за за этого история не нравилась. Одежда становилась влажной и дышать не хотелось.

Но однажды Историк придумал поставить каждому на парту баночку для мусора. Овальная банка из под Воймикса. И сказал - раз вам надо есть семечки и харкать, делайте это в банку. Только содержимое банок было выкидывать нельзя. И когда мы приходили на урок, у каждого на столе стояла воняющая банка в которую плевали прошлые классы уже несколько дней.

А еще историк запомнился фразой:
- Единственная причина не сделать домашнее задание - это справка из морга о вашей смерти!

ОБЖ

$
0
0
ОБЖ мне нравилось. На нём мы проводили репетицию атомной войны. Нам рассказывали про атомные взрывы, что во время взрыва лучше всего лечь в ванную. Она защитит от проникающей радиации и от упавшего перекрытия. И рассказывали как выжить в постапокалиптическом мире :) Про время распада радиоактивного йода. Рассказывали, какие таблетки надо поедать. Каждые пол года в городе объявлялась учебная тревога и мы ровными рядами бежали из школы до ближайшего бомбоубежища. Тревога реально внушала страх - вой сирен по всему городу.

Помимо атомной угрозы, в городе были еще больше запасы хлора и аммиака. В случае аварии, облака ядовитого газа закроют город. От хлора надо бежать на крыши, а от аммиака наоборот лечь на пол. И нас учили как в домашних условиях изготовить простейшую защиту органов дыхания.

И это мне пригодилось! Однажды дома я открыл банку с раствором хлора (или жидким хлором), мне стало плохо и я тут же выбежал из комнаты. У меня чуть глаза и лёгкие не "лопнули". И вот комната закрыта, я стою снаружи и думаю, а как оттуда банку забрать?

Я взял тазик, налил воды, растворил соду. В этом растворе вымочил полотенце. На голову одел прозрачный пакет, так что бы он закрывал только глаза. А рот (нижнюю часть головы) обмотал этим пропитанным содой полотенцем. И вполне комфортно пошел в комнату, открыл окна и закрыл банку.

Но ОБЖ-шник был типичным воякой. И всё у нас было как в армии, даже дедовщина :)

У нас в классе самыми важными были второ- и тертьегодники. Во первых они были испорченными, а это привлекало. Например, они закидывали мощные петарды, коробки с казеином или газеты пропитанные селитрой в окна переполненных отъезжающих автобусов. Во вторых, они всем рассказывали ощущения от приёма наркотиков и где их купить. В частности ощущения от Тарена, украденного из аптечек АИ-2, которые нам показывали на ОБЖ. Вокруг них собирались целы толпы учеников. А в третьих, их крышевали старшие братья, поэтому их даже коллективно бить было страшно. Гопники гороче.

И вот в один из обычных школьных дней, прямо в коридоре учебного заведения эти гопники начали пинать одного из одноклассников. Заставляли его маршировать и пинали под зад. А если он отказывался, то били уже кулаками.

По коридору идёт ОБЖ-шник и спрашивает:
- Что тут происходит?

Я думаю, ура, их накажут! Но ОБЖ-шик то же присоединился к ним и стал объяснять:

- Мы учим его маршировать!
- Молодцы! Что ты двигаешься как Буратино! Ноги выше!

И пнул его. Ну и вы понимаете, какая кличка была у человека после этого. Я тогда офигел. ОБЖ-шник был своим мужиком для гопников, он никогда не стучал завучу на класс. Даже когда один из блатных учеников прямо на уроке сказал:

- Ты говнюк! И что бы мне сделаешь?
- Да как ты со мной разговариваешь, я капитан второго ранга!
- Сифон у тебя второго ранга. Что ты сделаешь старая гнида?

ОБЖ-шник отвернул глаза и продолжил урок.

WinAPI

$
0
0
Вообще, я не люблю WinAPI. Я не люблю C++. :) Я верю в будущее C# и Java, функциональных языков программирования.

Но время и практика показывает насколько это гениальная вещь. Программистам, которые написали WinAPI и C++ надо поставить памятник.

MS всю свою историю пыталась похоронить WinAPI. Для программистов придумывали DDE, OLE, COM, COM+, DCOM, MFC, ATL, WTL. Платформы .NET сменялись одна за другой. MS обещала в ближайшем будущем похоронить WinAPI. "Программы для нового Windows можно будет писать только на новом API. Старые программы перестанут запускаться." (c)

Да, так оно когда нибудь и будет. Но WinAPI после каждого витка прогресса возвращается и MS пытается скинуть его.

Это же сколько лет то прошло. 20, 25?

C++ MS то же пыталась похоронить. Сравнивая развитие и возможности сред разработки C# и C++, другой мысли просто не может быть. :) Но вроде MS исправляется, последние версии уже начинают радовать.

Я же сейчас подумываю потихоньку от WinAPI отказываться. Суровая действительность говорит, что надо уже начинать писать программы, которые одновременно будут работать на WinAPI, Android, iOS. И слава богу, что под них можно писать используя C++ или Objective-C. Программы на Java все еще являются медленными и прожорливыми. И вообще после наездов Orcale (правообладателя) на пользователей Java я побаиваюсь смотреть в его сторону.

Вообще Orcale злые.

Есть еще JS... Но я поражаюсь, как можно было написать столь тормозной язык программирования. Недавно люди портировали Doom на JS. В Doom можно было играть в окне браузера. Только работал он на моём компе (4 ядра по 3 ГГц, причем у каждого ядра несколько конвееров, 8 Гб ОЗУ, видеоплата с процессором ~200 ядер) медленне, чем на 486-ом процессоре.

JS выполняется везде. Это плюс. Но его возможности огорчают.

Флеш почти похоронили.

Эффект на кнопке

$
0
0
Рассматривая все новые программы, в том числе сложные и специализированные, все без исключения люди пускают слюни от стартовой страницы и интерфейса в общем. Эта мысль у меня появилась еще в глубоком детстве. Всем нравились игры с хорошей графикой, а на геймплей всем было пофиг. Собственно сейчас так и происходит. Начиная от самой популярной игры на планшетах Bad Piggies, заканчивая супер 3D шутерами.

(Ну не всем, а половине. Остальной половине нравится годами кликать по монстрам в MMORPG. Геймплеем это то же назвать сложно.)

Так и в бизнес приложениях. Я сам видел, как взрослые дяди: бухгалтера, финансисты, директора выбрали программу, в которой было больше всего эффектов, переливающихся графиков. Когда при щелчке по ячейке отчета не просто появляется новый отчет, а с дымом и искрами разворачивается эта ячейка.

Короче, нам и мне заняться всякими украшательствами. В первую очередь я заметил переливающиеся кнопки на панели задач Windows... 8 минут и у меня то же есть подобный эффект



В не оптимизированном, но более понятном варианте это выглядит так:

      int mx,my;
      getCursorPos(mx,my);
      screenToClient(mx,my);
      unsigned char* a = (unsigned char*)dc.backBmp.ptr32;
      if(a==0) return;
      double cy = dc.backBmp.height;
      double cx = dc.backBmp.width;
      for(int y=dc.backBmp.height-1; y>=0; y--)
        for(int x=0; x<dc.backBmp.width; x++) {
          int xx = abs(cx-x-mx)*cy/cx + abs(cy-y-my);
          xx = 16-xx;
          if(xx<0) xx=0;
          for(int n=0; n<3; n++) {
            int v = int(*a) + xx;
            if(v > 255) v = 255;
            *a++ = v; 
          }
          a++;
        }

БК0011М

$
0
0
Процессор:К1801ВМ1 4 МГц 16 бит (система команд PDP11)
ОЗУ:128 Кб
Графика:512x256 2 цвета, 256x256 4 цвета из 8 цветов, 2 видеостраницы
Выпускался:   с 1990 года.



БК0011М - это работа над ошибками БК0011, характеристики компьютера не изменились. Нового видеорежима, который ждали все, так и не появилось. И это последняя БК-шка из серии.

В прошлом компьютере БК0011 (без М)увеличили объем памяти с 32 Кб до 128 Кб, что больше адресного пространства процессора, поэтому пришлось делать переключение страниц. А управление страницами памяти добавили в регистр, которой в БК0010 управлял магнитофоном/звуком. И что бы старые программы случайно не переключили страницу, то есть что бы сохранить с ними совместимость, инженеры запретили изменение соответствующего регистра старым программам. Запретили целиком, поэтому звук пропал. (Правильнее усложнили, а не запретили)

В компьютере БК0011М этот косяк исправили, старые программы могли изменять отдельные биты управляющие звуком.

Так же поправили несколько мелких ошибок и переписали ПЗУ компьютера.

В отличии от (моего) БК0011, в этом БК0011М применена другая клавиатура. В прошлом была кнопочная (с ужасным дребезгом), а в этом кнопочно-плёночная. Дребезга стало меньше, но и надежность пострадала. На этой клавиатуре некоторые кнопки не работают, потому что дорожки на пленке потрескались.

Слева под крышкой должна находится плата с 3-мя микросхемами ПЗУ, в которые прошит Бейсик и двумя панельками для установки ПЗУ с расширениями. К сожалению вандалы вытащили эту плату и у меня её нет.

Все фото кликабельны. Теперь я снимал под мягким светом, бликов нет вообще. Еще бы штативчик найти, для фотографирования сверху-вниз. А то при съемке из руки качество страдает.





Видимо на производстве краска кончилась и низ корпуса получился желтым с черным продресью. Шильдик потерялся :(



Вандалы выкусали все КМ-ки (конденсаторы) с платы. Два последних вечера я впаивал современные (китайские) конденсаторы. Обычно я запаивал КМ-ки, но запас КМ-ок закончился.



(Картинка ниже в 4 раза больше)
















Получилось так, что этот компьютер я поменял на TI99/4A (тот который умел говорить). Я решил собрать все советские компьютеры, а этот американский. Ему в этой коллекции было одиноко. :)


Ну и конечно же игры:





P.S. Вы можете использовать любые мои фотографии в некоммерческих целях, если в правом нижнем углу подпишите VINXRU.

Товарищ, смелее приближай Ядерный Апокалипсис!

$
0
0
В преддверии термоядерной войны я решил почитать, что нового изобрели ученые и политики. Ведь атомные бомбы изобрели в 1950-х годах. Пора изобретать новое оружие. Представьте технику 1960-х годов! И атомные бомбы прямиком оттуда. Нам нужны новые бомбы 21-ого века. Они будут еще мощнее, еще ядовитее, еще разрушительнее! При этом будут обладать интеллектом и никакая ПРО их не остановит. И это будущее уже не за горами:

Во первых, договор СНВ-III был подписан Россией и США в Праге в 2010г можно считать разорванным. В течении 10 лет стороны должны сократить ядерные боезаряды до 1550 единиц, а межконтинентальные баллистические ракеты подводных лодок и тяжелых бомбардировщиков - до 700 единиц. Поскольку США разорвало отношения между военными ведомствами США и России, нормальных регулярных двухсторонних контактов по соблюдению договоров быть не может и договор можно считать разорванным.

Во вторых, Россия и США давно уже проводят ядерные испытания. Только теперь они называются "неядерными ядерными испытаниями"или "подкритическими ядерными взрывами". Суть в том, что бомба содержит недостаточное для цепной реакции количество радиоактивного материала. После нажатия на красную кнопку, все так же происходит химический взрыв приводящий к обжатию плутония. Так же ускоряется расщепление плутония, но ускоряется недостаточно для моментального взрыва.

Ученые из такой модели могут получить гораздо больше полезных сведений, чем от полноценного взрыва. После взрыва от бомбы ничего не остается. А в случае подкритичного ядерного испытания, обжатый плутоний можно исследовать и использовать в следующей бомбе. Можно однозначно спрогнозировать силу ядерного взрыва.

Но и мирный атом не стоит на месте. Китай продает ядерные светящиеся брелки, их уже все видели. А вот ядерные батарейки, я увидел сегодня впервые. Такую бы в смартфон!

H-DOLBAQ1zE[1]

http://item.taobao.com/item.htm?spm=a1z10.1.4004-56656384.29.dysKyM&id=15659486189

Говорят, что батарейка дает 3 Вольта, 50.0 мкВт / 3 = 16 мкА в течении 20 лет. Для такой цены это мало. Но все когда-то начиналось с заоблачных цен и микроскопического кпд.

Эффект на кнопке

$
0
0
Рассматривая все новые программы, в том числе сложные и специализированные, все без исключения люди пускают слюни от стартовой страницы и интерфейса в общем. Эта мысль у меня появилась еще в глубоком детстве. Всем нравились игры с хорошей графикой, а на геймплей всем было пофиг. Собственно сейчас так и происходит. Начиная от самой популярной игры на планшетах Bad Piggies, заканчивая супер 3D шутерами.

(Ну не всем, а половине. Остальной половине нравится годами кликать по монстрам в MMORPG. Геймплеем это то же назвать сложно.)

Так и в бизнес приложениях. Я сам видел, как взрослые дяди: бухгалтера, финансисты, директора выбрали программу, в которой было больше всего эффектов, переливающихся графиков. Когда при щелчке по ячейке отчета не просто появляется новый отчет, а с дымом и искрами разворачивается эта ячейка.

Короче, нам и мне заняться всякими украшательствами. В первую очередь я заметил переливающиеся кнопки на панели задач Windows... 8 минут и у меня то же есть подобный эффект



В не оптимизированном, но более понятном варианте это выглядит так:

      int mx,my;
      getCursorPos(mx,my);
      screenToClient(mx,my);
      unsigned char* a = (unsigned char*)dc.backBmp.ptr32;
      if(a==0) return;
      double cy = dc.backBmp.height;
      double cx = dc.backBmp.width;
      for(int y=dc.backBmp.height-1; y>=0; y--)
        for(int x=0; x<dc.backBmp.width; x++) {
          int xx = abs(cx-x-mx)*cy/cx + abs(cy-y-my);
          xx = 16-xx;
          if(xx<0) xx=0;
          for(int n=0; n<3; n++) {
            int v = int(*a) + xx;
            if(v > 255) v = 255;
            *a++ = v; 
          }
          a++;
        }

Переезд.

$
0
0
Перенесу все материалы по ретрокомпьютерам в вконтакт. Там же будут новые статьи.

http://vk.com/retropk

Сожаления не принимаются :)

Цветные линии для Специалиста

$
0
0
https://github.com/vinxru/OldLines/tree/master/Lines_Specialist

Написал игру Цветные Линии для Специалиста.

Разрешение 384x256. А ведь разрешение одно из самых больших и цветных среди всех советских компьютеров. Круче только у Корвета и УКНЦ. Это даже больше чем 320x200, это все любимые игры Doom II, Dune II, UFO...).

Но всего 8 цветов и есть ограничения. В блоке 8x1 пикселей может быть только два цвета, один из которых черный.





Очень неплохой компьютер был. Причем, один из самых маленьких и без дефицитных микросхем.

Новая панель инструментов

$
0
0
Сегодня в своей программе сделал большую панель инструментов. Лично мне цветные значки нравятся больше, но народ в восторге.



Там еще есть кое какие анимационные эффекты, но видео лень снимать.

Гигантский плюс такой графики в том, что её быстро рисовать. Даже я смогу это нарисовать.

Я решил уделять больше времени внешнему виду, ведь я пишу интерфейс пользователя :)

Судя по последним тенденциям пользовательских интерфейсов Windows 3.1 возвращается, что лично мне очень нравится. Всё белое и нет кнопки ПУСК.

Windows 8


Windows 3


UPD:

Не Norton Commander для SD контроллера Специалиста

$
0
0
Исходники: https://github.com/vinxru/Specialist_SD_Controller/tree/master/%D0%A4%D0%B0%D0%B9%D0%BB_shell_Rks

Написал еще файловый менеджер для своего контроллера. А в сам контроллер добавил функцию подсчета свободного места на диске, одновременно два открытых файла, командную строку, точки входа в ОЗУ (а не в ПЗУ).

Подсчет места на диске длится секунд двадцать :) Что бы посчитать место, надо всю таблицу FAT размером в несколько мегабайт прогнать сквозь микроконтроллер. А это не быстро.

Командер сейчас может:
- Показывать в двух панелях файлы и папки. Имя 8+3. Отображает размер файла, дату и время создания.
F1) Показать размер диска и свободное место (по стандарту CTRL+L)
F2) Создать новый текстовый файл (по стандарту SHIFT+F4)
F3) Запуск программы view.rks с выбранным файлом в командной строке.
F4) Запуск программы edit.rks с выбранным файлов в командной строке.
F5) Копирование файла, по умолчанию в папку на другой панели без изменения имени. Но имя и путь можно вводить вручную.
F6) Перенос файла или папки...
F7) Создание папки
F8) Удаление файла или пустой папки.

Не может, но думаю сделать:
- Удалять папку с файлами
- Копировать папку
- Выделять несколько файлов
- Не поддерживается маска в файловых операциях (*.txt)
- Не поддерживаются папки .. и . в путях
- Поиск файла на диске
- Запуск программ по расширениям
- Все горячие клавиши типа Ctrl+O, Ctrl+L, Ctrl+Enter.
- Поиск через ALT+буква














Окончательный стандарт SD контроллера

$
0
0
Переделал все на новый стандарт. Осталось командер отладить (глючит). Ну и потом добавить эмулятор магнитофона.

Работает на удивление шустро. Скорость можно глянуть тут:


UPD: Надо отключать автоматическую регулировку яркости при съёмке монитора :)

Сначала запускается SD STARTER, программа размером меньше 100 байт. Она всего лишь запускает файл SD BIOS (boot/sdbios.rks).

Идея в том, что SD STARTER потом будет загружаться через порт магнитофона (эмулятором магнитофона спрятанном внутри контроллера). И хранится на флешке в файле boot/boot.rks. Благодаря этому не требуется изменение стандартного ПЗУ компьютера. То есть не нужно дорабатывать компьютер. Скорость загрузки с магнитофона не очень большая, всего 150 байт/сек, поэтому эту программу я сделал максимально маленькой.

Но лично я засунул SD STARTER в ПЗУ. Так компьютер запускается быстрее и порт магнитофона остается свободным. Самое большое неудобство программ в ПЗУ, это невозможность исправлять ошибки. И тут маленький объем стартера (и минимум функций) как нельзя кстати. Меньше вероятность наделать ошибок. Новые функции в нем никогда не появятся. И я надеюсь, что мне его никогда не придется переделывать.

Что не скажешь о SD BIOS-е. Это набор подпрограмм для работы с контроллером.

Стартер запускает БИОС. БИОС получает версию контроллера, выводит её на экран. То есть на экране видно три строки:

SD STARTER 1.0
SD BIOS 1.0
SD CONTROLLER 1.0

БИОС состоит из двух частей. Резидентной, которая всегда должна находится в памяти. Это собственно подпрограммы для работы с контроллером. И часть, выполняющая запуск компьютера, которая потом уничтожается. Эта часть рисует на экране изображение флешки, что то пишет и запускает файл boot/shell.rks, который является командором.

Вместо любого файла (boot, sdbios, shell) можно подсунуть любую игру/программу. Она будет работать, только БИОСА в памяти не будет. А он может быть и не нужен.

После запуска БИОСА карта памяти выглядит так:

С самого начала памяти свободно 35 537 байт памяти (до 8AD1h). Дальше находятся переменные SELF_NAME, CMD_LINE. Если они не нужны программисту, то объем непрерывной свободной памяти составляет ~36 049 байта (до 8CD1h).

8AD1h SELF_NAME      Собственное имя файла 256 байт (этот адрес содержится в DE после запуска)
8BD1h CMD_LINE       Командная строка 256 байт (этот адрес содержится в HL после запуска)
8CD1h SD_BIOS_CODE   Код SD BIOS 559 байт (возможно будет расти в начало сдвигая SELF_NAME и CMD_LINE)
8F00h -              Не используется ~233 байта (возможно тут есть переменные монитора)
8FDFh MONITOR_VARS   Переменные монитора
9000h VIDEO_MEM      Видеопамять 


Свободное место по адресу 8F00h оставлено на всякий случай. Во первых, что бы не конфликтовать с Монитором. Возможно, что будущие версии SD BIOS будут использовать эту память.

SD BIOS может быть в любом месте памяти. При запуске программы регистр A содержит версию набора команд (сейчас 1). BС содержит точку входа. HL командную строку. DE собственное имя.

Для вызова функции контроллера, надо поместить в регистр A код функции и вызвать подпрограмму по адресу переданному при старте программы в регистре BC.

Функции контроллера:

Reboot      Теплая перезагрузка (A=0, HL="", DE="" / A=код ошибки)
Exec        Запустить программу (A=0, HL=имя файла, DE=командная строка  / A=код ошибки)
FindFirst   Начать получение списка файлов (A=1, HL=путь, DE=максимум файлов для загрузки, 
                                                 BC=адрес / HL=сколько загрузили, A=код ошибки)
FindNext    Продолжить получение списка файлов (A=1, HL=":", DE=максимум файлов для загрузки, 
                                                 BC=адрес / HL=сколько загрузили, A=код ошибки)
Open        Открыть файл       (A=2, D=0,   HL=имя файла / A=код ошибки)
Create      Создать файл       (A=2, D=1,   HL=имя файла / A=код ошибки)
MkDir       Создать папку      (A=2, D=2,   HL=имя файла / A=код ошибки)
Delete      Удалить файл/папку (A=2, D=100, HL=имя файла / A=код ошибки)
Seek        Установить позицию чтения записи файла (A=3, B=режим, DE:HL=позиция / A=код ошибки, DE:HL=позиция)
               (С начала B=0, с текущего положения B=1, с конца B=2)
GetSize     Получить размер файла (A=3, B=100 / A=код ошибки, DE:HL= размер файла)
Swap        Работа с двумя открытыми файлами (A=3, B=200 / A=код ошибки)
Read        Прочитать из файла (A=4, HL=размер, DE=адрес / A=код ошибки, HL=сколько загрузили)
Write       Записать в файл (A=5, HL=размер, DE=адрес / A=код ошибки)
WriteEOF    Конец файла (A=5, HL=0 / A=код ошибки)
Move        Переместить файл/папку (A=6, HL=из, DE=в / A=код ошибки)
GetFree     Размер флешки и свободное место на флешке (A=7, HL=буфер / A=код ошибки) 



Операционная система RAMFOS для компьютера Специалист

$
0
0
https://github.com/vinxru/RAMFOS

Последнее время я дизассемблировал и изучал операционную систему RAMFOS компьютера Специалист МХ2. В результате нашел десяток ошибок и недоработок. Получившийся исходный код вы можете посмотреть тут. Хочется еще добавить поддержку SD, но не могу найти места.





Все мои поправки включаются/выключаются через опции. Т.е. отключив всё можно получить стандартную ОС:

- Запуск ОС с вставленной карты памяти
- Вывод логотипа "Специалист MX2"во время загрузки
- Размещать дату в основной оперативной памяти (возможна работа без ДОЗУ)
- Запускать RKS файлы (программы стандартного Специалиста)
- Если нажать SHIFT, то RKS файл запускается в режиме MX
- Стандартные программы (редактор, ассемблер, дизассемблер) в виде файлов, а не в ПЗУ.
- Отключить принтер
- Отключить загрузку с дискеты (в частности MXDOS)
- Открывать файл с любым расширением (А не только TXT в редакторе)
- Печать всех символов знакогенератора через ESC
- Использовать все 64 Кб ПЗУ Специалиста МХ2 под ROM-диск
- Если ДОЗУ не найдено, то использовать буфер в ОЗУ размером с эту переменную
- Первый байт страницы ПЗУ
- Максимальное кол-во страниц расширенной памяти (максимум 0Fh, т.е. 1 Мб)
- При запуске показать ROM или RAM
- Ускорение вывода текста в 3 раза. Так же выводит текст строки без разрывов. И не портит цвета символов правее.
- Исправленная функция определения обьема ДОЗУ
- Новый интерфейс
- Поддержка цвета

SD контроллер для Радио 86РК и Апогея БК01 в частности

$
0
0
Давно, давно, давно надо было сделать этот контроллер. Но в начале этого года отвлек меня SysCat, подарив плату Специалиста. :) И все же возвращаюсь. Первая версия платы выглядит так, схема в самом низу.



Все просто. Контроллер подключается к параллельному порту. Контакты расположены так, что бы подключаться к Апогею БК01. А внутри МК находится эмулятор ПЗУ и запустить программу оттуда можно с помощью стандартной директивы R0,100 G. Загруженная программа уже будет работать с драйвером файловой системы FAT32, которые так же находится внутри МК.

Протокол будет таким:

Сначала на шину адреса устанавливаем значения 19, 23, 87. Они отличаются одним битом, поэтому шума между ними быть не должно.

Далее, компьютер читает адреса 86,87,86,87,86,87... каждый байт 86 является тактовым импульсом, т.е. подтверждением приема/передачи байта.

Если синхронизация удалась, то МК выставляет на шину данных ERR_START, а после тактового импульса ERR_OK_WAIT. Код ERR_OK_WAIT может держаться неопределенное кол-во тактов. В это время происходит инициализация флешки.

Потом ERR_OK_WAIT меняется на ERR_OK_NEXT если все нормально. Или возвращается код ошибки.

Компьютер должен переключить порт на выход и передать команду. В примере ниже команда BOOT - единственный байт 0

На это МК отвечает ERR_OK_WAIT (много раз), ERR_OK_ADDR, адрес загрузки L, адрес загрузки H, ERR_OK_WAIT (много раз)

Потом выдает содержимое файла блоками: 0, длина L, длина H, данные (HL раз), ERR_OK_WAIT (много раз)

Если данных не осталось, выдает ERR_OK_READ или код ошибки

Любое значение на шине адреса кроме 86, 87 вызовет отключение SD контроллера и включение эмулятора ПЗУ. Поэтому начальный загрузчик всегда можно загрузить директивой R0,100

Код:	

     .org 0h

DATA_PORT       = 0EE00h
ADDR_PORT       = 0EE01h
CTL_PORT        = 0EE03h
SEND_MODE       = 10001011b ; Настройка: 1 0 0 A СH 0 B CL   1=ввод 0=вывод
RECV_MODE       = 10011011b

ERR_START         = 040h
ERR_WAIT          = 041h
ERR_OK_DISK     = 042h
ERR_OK          = 043h
ERR_OK_READ       = 044h
ERR_OK_ENTRY     = 045h
ERR_OK_WRITE     = 046h
ERR_OK_ADDR    = 047h

Entry:
     ; Первым этапом происходит синхронизация с контроллером
     ; 256 попыток. Для этого в регистр C заносится 0

     ; А в стек заносится адрес перезагрузки 0C000h

     LXI   B, 0F800h
     PUSH   B     

     JMP   Boot

     NOP

;----------------------------------------------------------------------------
; Отправка и прием байта

Rst1:
     INX   H      ; HL = ADDR_PORT
     MVI   M, 86
     MVI   M, 87
     DCX   H      ; HL = DATA_PORT
     MOV   A, M
     RET

;----------------------------------------------------------------------------
; Ожидание готовности МК

Rst2:
WaitForReady:
     Rst   1
     CPI   ERR_WAIT
     JZ      WaitForReady
     RET

;----------------------------------------------------------------------------

RetrySync:
     ; Попытки
     DCR C
     RZ            ; Ошибка синхронизации, перезагрузка

Boot:
     ; Режим передачи (освобождаем шину) и инициализируем HL
     MVI   A, RECV_MODE
     CALL SetMode

     ; Начало любой команды
     MVI   M, 19
     MVI   M, 23
     MVI   M, 87

     ; Если есть синхронизация, то контроллер ответит ERR_START
     Rst   1         
     CPI   ERR_START
     JNZ   RetrySync

     ; Дальше будет ERR_OK_WAIT, ERR_OK_NEXT
     ; Инициализация флешки
     Rst   2
     CPI   ERR_OK_ADDR
     JNZ   Rst1      ; Ошибка, освобождаем шину и перезагрузка

     ; ERR_OK_NEXT высталенный МК будет висеть на шине до следующего RST,
     ; только после него МК освободит шину и мы сможем включить режим передачи.
     Rst   1     

     ; Режим передачи     
     MVI   A, SEND_MODE
     CALL SetMode

     ; Код команды
       XRA   A
     Rst   1

     ; МК читает данные во время тактового импульса, т.е. он уже их прочитал.
     ; Включаем режим приема, т.е. освобождаем шину.

     ; Режим приема
     MVI   A, RECV_MODE
     CALL SetMode

     ; МК захватит шину во время тактового импульса (первого RST)

     ; Дальше будет ERR_OK_WAIT, ERR_OK_RKS
     Rst   2
     CPI   ERR_OK_RKS
     JNZ   Rst1      ; Ошибка, освобождаем шину и перезагрузка
     
     ; Удаляем из стека 0F800h
     POP   B

     ; Адрес загрузки в BC
     Rst   1
     MOV   C, A
     Rst   1
     MOV   B, A

     ; Сохраняем в стек адрес запуска
     PUSH   B

     ; Подождать, пока МК прочитает очередной блок денных
RecvLoop:
     Rst   2
     CPI   ERR_OK_READ
     JZ      Rst1      ; Всё загружено, освобождаем шину и запуск
     ORA   A
     JNZ   0F800h   ; Ошибка, перезагрузка (не отпускаем контроллер)

     ; Принять очередной блок
     Rst   1
     MOV   E, A
     Rst   1
     MOV   D, A
RecvBlock:
     Rst   1
     STAX   B
     INX   B
     DCX   D
     MOV   A, E
     ORA   D
     JNZ   RecvBlock
     JMP   RecvLoop

      ; Прием/передача
SetMode:
     LXI   H, CTL_PORT
     MOV   M, A
     DCX   H
     DCX   H      ; HL = ADDR_PORT
     RET
.End



108 байт.

Схема

(RESET забыл :)

Операционная система MXOS компьюетра Специалист МХ

$
0
0
https://github.com/vinxru/MXOS

Исправленные недоработки.

В файле DOS.SYS
● BIG_MEM - Поддержка ДОЗУ большего объема, чем 64 Кб
● ROM_64K - Размер ПЗУ у Специалиста MX2 всего 64 Кб, но без этой опции будет работать лишь 32 Кб
● LOAD_FONT - Загружать шрифт в ОЗУ (ускорение работы и возможность загрузки ОС с любого накопителя)
● DISABLE_COLOR_BUG - Включить инициализацию контроллера цвета при запуске

В файле NC.COM
● ENABLE_COLOR - Включить цвет
● BIG_MEM - Поддержка ДОЗУ большего объема, чем 64 Кб
● DISABLE_FREE_SPACE_BUG - Исправить ошибку определения свободного объема


О чем я вообще?


MXOS - это название операционной системе я дал сам, поскольку оригинального названия я не нашел. Народ её называл командер, а внутри оперционки содержатся лишь строки: BIOS 4.40, COMMANDER VERSION 1.4, (C) OMSK 1992.

MXOS это альтернативная операционная система для компьютера Специалист МХ. Оригинальна операционная система называлась RamFOS.

В отличии от RamFOS эта операционная система написана более грамотно. Для доступа к накопителям используются драйвера. Чтение и запись происходит 256 байтными блоками. В RamFOS доступ был побайтный и при том частый и хаотичный, что не позволяло подключить дисковд или SD карту в качестве накопителя.

Так же MXOS более шустрая и обладает более приятным интерфейсом напоминающим Norton Commander. Любое расширение файла можно привязать к любой программе. Список соответствий хранится в файле NC.EXT, максимальный размер которого 36 Кб.

Но при этом MXOS содержит меньше сервисных возможностей и не совместима с RamFOS и стандартным Специалистом. Основные отличия, это:

● Нет поддерживаемых системой верхней и нижней строки состояния
● Система не содержит форму для открытия файла
● Нет поддержки звука на основе ВИ53
● Нет поддержки цвета (хотя и RamFOS его полноценно не поддерживал и не использовал вообще)
● Поддерживается значительно меньше специальных кодов при выводе на экран
● Поддерживается лишь одна модель принтера
● Оригинальная ОС поддерживает лишь 64 Кб дополнительного ОЗУ (что в сумме дает 128 Кб), которое используется как RAM-диск. Запуск с большим объемом памяти приведет к зависанию, так как в порт выподра страницы записывается случаное число. Исправленная версия ОС поддерживает 448 Кб дополнительной памяти, т.е. 7 страниц как накопители B-H.

Многие возможности можно реализовать дополнительными модулями. Например, в комплекте идет драйвер ПЗУ подключаемого к порту расширения. Таким же образом могла быть выполнена поддержка дисковода, но драйвера у меня нет.

Поддерживаются две кодировки KOI-7 и KOI-8, переключаемые как с клавиатуры, так и ESC-последовательностями (ESC+'('и ESC+')'). Знакогенератор содержит 256 символов.

Используется раскладка клавиатуры Стандартного специалиста. У MX отличаются коды клавиш F1-F10, TAB, ESC.

MXOS поддерживает BAT-файлы и передачу аргументов запускаемым программам. При холодной перезагрузке запускается файл B:AUTOEX.BAT, затем A:FORMAT.COM B:, а затем A:NC.COM. Устройство A: - это ПЗУ, устройство B: - это оперативная память. Максимально поддерживается 8 устройств.

При создании собственного ПЗУ (загрузочного диска A:) вы можете разместить AUTOEX.BAT так же и на диске A:, а в нем разместить запуск драйверов.

Запуск MXOS


Если при запуске компьютера зажать клавишу ?, то MXOS сразу перейдет к загрузке программы с магнитофона. Если при запуске компьютера зажать клавишу ?, то MXOS пропустит запуск B:AUTOEX.BAT.

При перезагрузке инициируемой программами запускается лишь A:NC.COM.



Имя файла состоит из 6+3 символов. Ввод расширения при запуске файла из ком. строки обязателен. Папки самой операционной системой не поддерживаются, но это можно реализовать через драйверы. Максимальное кол-во файлов в папке - 48 шт, но оболочка поддерживает отображение лишь 36 файлов. Остальные файлы вы не увидите, не сможете выполнять над ними действия из оболочки, но сможете запустить их ком строки. Максимальный размер файла - 64 Кб, но оболочка может работать лишь с ~36 Кб. При попытке скопировать (и т.п.) файл большего размера произойдет переполнение буфера, уничтожение системных переменных, затем экрана, затем самой ОС в зависимости от размера файла. Структура хранения файлов напоминает FAT, только используются 8 битные номера кластеров.

В отличии от "монитора"стандартного Специалиста, MXOS не содержит режима работы с консоли и соответственно директив вводимых с клавиатуры. Вся работа происходит в диалоговом режиме.

Оболочка поддерживает две панели, как Norton Commander.



Клавиши выполняют следующие команды:

● ESC - Очистка ком строки
● TAB - Переход между панелями
● F1, F2 - Выбор накопителя для левой и правой панели
● F3 - Отображение на неактивной панели информации о накопителе активной панели (как на фото выше)
● F4 - Запуск внешнего редактора E.COM для выбранного файла
● F5 - Копирование файла
● F6 - Изменение имени файла / перемещение
● F7 - Загрузка файлов с ленты (магнитофона) на накопитель
● F8 - Удаление файла
● F9 - Сохранение файла с накопителя на ленту

Есть и другие клавиши, но я пока не разбирался

Раскрасил командер



Добавил ключ ENABLE_COLOR, который включает эту раскраску.




Ядро (DOS.SYS)



Представляет собой набор подпрограмм по адресу C800h. Это подпрограммы обслуживания
экрана, принтера, клавиатуры и накопителя на магнитой ленте и файловой системы.



Зеленым отмечены функции соврапающие со стандратным Специалистом. Но галвная проблема
невосместимости - Аппаратная. Адрес порта клавиатуры у MX изменен.



C800hrebootЗапуск файла A:NC.COM
C803hgetchВвод символа с клавиатуры; A-код
C806htapeReadВвод байта с ленты; A-код; если ошибка, то происходит переход на адрес по адресу 8FE1h
C809hprintCharВывод байта на экран; C-код
C80СhtapeWriteВывод байта на ленту; C-байт
C80FhinputВвод строки с клавиатуры
C812hkeyScanВвод кода нажатой клавиши; A-код; иначе-0FFh
C815hprintHexByteВывести 16-ричное число на экран; A-число
C818hprintStringВывод строки символов на экран, до нулевого байта; HL-начало строки
C81BhkeyScanВвод кода нажатой клавиши; A-код; иначе-0FFh
C81EhgetCursorPosПолучить координаты курсора в HL (координаты в пикселях)
C821hsetCursorPosУстановить координаты курсора из HL (координаты в пикселях)
0C824htapeLoadЗагрузить программу с ленты
0C827htapeSaveСохранить программу на ленту
0C82AhcalcCrcРасчет контрольной суммы
C82DhprintHexWordВывести 16-ричное число на экран; HL-число
C830hgetMemTopПолучить объем доступной памяти; HL-объем
C833hsetMemTopУстановить объем доступной памяти; HL-объем
C836hprinterВывод байта на принтер
C838h-Переход на 0C800h
C83Сhreboot2Запустить A:NC.COM (стандартную точку С800h можно изменить)
C83FhfileListПолучить список файлов
C842hfileGetSetDriveПолучить/установить активное устройство
C845hfileCreateСоздать файл
C848hfileLoadЗагрузить файл по адресу из заголовка этого файла
C84BhfileDeleteУдалить файл
C84EhfileRenameПереименовать файл
C851hfileLoadInfoЗагрузить информацию о файле
C854hfileGetSetAddrПолучить/установить адрес загрузки файла
C857hfileGetSetAttrПолучить/установить атрибуты файла
C85AhfileNamePrepareПреобразовать имя файла во внутренний формат
C85DhfileExecЗапустить файл
C860hinstallDriverУстановить драйвер накопителя
C863hdiskDriverДрайвер выбранного диска
C866hfileLoad2Загрузить файл по адресу


Стандартные служебные ячейки



8FE1h2tapeErrorАдрес куда происходит переход при ошибке чтения с ленты
8FE3h2tapeAddrАдрес программы загруженной с ленты
8FE7h2charGenАдрес альтернативного знакогенератора; адрес необходимо разделить на 8
8FE9h1cursorCfgВнешний вид курсора (7 - бит видимость, 654 - положение, 3210 - высота)
8FEAh1koi80FFh=включен KOI-8, 0=включен KOI-7
8FEBh1escModeОбработка ESC-последовательности
8FECh1keyLocksСостояние клавиш CAPS LOCK и РУС/LAT
8FEFh2lastKeyДве последние нажатые клавиши
8FF1h2beepДлительность и частота звукового сигнала
8FF4h1repeatЗадержка повтора клавиш
8FFAh2inverse0=нормальный текст, 0FFFFh=инверсный текст
8FFCh1cursorYПоложение курсора по вертикали в пикселях
8FFDh1cursorXПоложение курсора по горизонтали в пикселях / 2
8FFEh1writeDelayСкорость при записи на ленту
8FFFh1readDelayСкорость при чтении с ленты


Карта памяти



0000h - 8FDEh36830 байтСвободная память
8FDFh - 8FFFh33 байтаСистемные переменные
9000h - BFFFh12 КбЭкран
C000h - СFFFh4 КбDOS.SYS (после ~CE94h свободно)
D000h - E1FFh4608 байтNC.COM (после ~E11Bh свободно) / FORMAT.COM
E200h - FAFFh6 КбРезерв под драйвера
FB00h - FDFFh768 байтДисковый буфер
FF00h - FF81h130 байтКоммандная строка. Заполняется fileExec
FF82h - FFC0h62 байтаСтек
FFC0h - FFEFh32 байтаDOS.SYS
FFD0h - FFFFh32 байтаОборудование


Формат файловой системы FAT8



Оригинальная ОС поддерживает лишь 64 Кб ДОЗУ и при запуске с большим объемом зависнет. Это происходит из за того, что
в регистр страницы ДОЗУ записывается случайное значение.

Исправленная ОС (опция BIG_MEM) по умолчанию 7 первых страниц ДОЗУ отображает как накопители B-H.</p>

Операционная система адресует диски 256 байтными блоками. А так как последние 64 байта адресного пространства всегда занимают
основное ОЗУ и устройства, то целых блоков получается 255.

То есть операционная система не использует последние 192 байта памяти. Только NC.COM в конце нулевой страницы (FF00h-FF0Ah) хранит своё состояние.

Файловая система подобна FAT. Накопитель максимальным объемом 64 КБ разбит на 256 блоков по 256 Кб.

Первые 4 блока содержат служеюную информацию. Нулевой блок таблицу FAT, следующие 4 блока - каталог.

Таблица FAT содержит 256 чисел. Число 5 по адресу 8, значит что за 8 блоком следует читать 5-ый блок. Последний блок замыкается сам на себя, то есть у последнего блока в ячейке 7 должно быть число 7. Свободным блокам в таблице FAT соответствует число 0. Первые 4 числа в таблице не используются, как и не используются блоки с нмоерами 0-3 для хранения файлов.

Каталог находящийся в блоках 1-3 содержит список файлов. 48 файлов по 16 байт на каждый. Если первый байт имени файла FFh,
значит файл не существует.

Структура записи следующая:

6 байт - имя файла
3 байта - расширение файла
1 байт - атрибуты файла
2 байт - адрес загрузки файла
2 байт - длина файла - 1
1 байт - ?
1 байт - первый кластер файла


После включения компьютера процессор начинает выполнять программу с начала ПЗУ. Эта ПЗУ имеет такой же формат как и ДОЗУ и
представлена в системе диском A:

Первые 4 байта ПЗУ, то есть таблицы FAT содержат команду перехода. Сам же загрузчик может обычно размещаться в конце каталога. Если первый символ имени файла FF, то остальные 15 байт записи могут содержать произвольные данные.

Но загрузчик можно размещать в любом блоке, который может быть даже помечен как свободный. При использовании неизменного ПЗУ это никогда не приведет к ошибке.

В оригинальной системе ПЗУ по адресам 800h-FFFh олжно содержать знакогенератор. Перед выводом каждого символа на экран, этот
символ будет копировать из ПЗУ в ОЗУ. Что не только медленно, но и не позволяет отвязать систему от ПЗУ.

И еще хвала TASM :)



Здорово, что в tasm есть макросы из языка Си. Код получается на порядок понятнее.

#define G_WINDOW(X,Y,W,H) .db 2, Y, 90h+(X>>3), H-6, (W>>3)-2
#define G_HLINE(X,Y,W) .db 1, Y, 90h+(X>>3), (((X&7)+W+7)>>3)-2, 0FFh>>(X&7), (0FF00h>>((W+X)&7)) & 0FFh
#define G_VLINE(X,Y,H) .db 3, Y, 90h+(X>>3), H, 80h>>(X&7)

g_filePanel:	G_WINDOW(0, 0, 192, 230)	; было 2, 0, 90h, 0E0h, 16h
		G_HLINE(4, 208, 184)		; было 1, 0D0h, 90h, 16h, 0Fh, 0F0h
		G_VLINE(96, 3, 205)		; было 3, ?, 9Ch, 0CDh, 80h
		.db 0

g_infoPanel:	G_WINDOW(0, 0, 192, 230)	; было 2, 0, 90h, 0E0h, 16h
		G_HLINE(4, 31, 184)		; было 1, 1Fh, 90h, 16h, 0Fh, 0F0h

Цветные Линии для Ориона-128

$
0
0
Исходники GITHUB
Скачать готовую игру lines$.bru



У компьютера Специалист есть брат под названием Орион 128. Правильнее сказать, что Орион потомок Специалиста. Но слишком уж у них похожие параметры и схемотехника. Адаптировать версию для Специалиста оказалось очень просто.

Специалист
Процессор КР580ВМ80 2 Мгц
Видео 384x256 2/4/8/16 цветов*
Основное ОЗУ 32..62 Кб
Дополнительное ОЗУ 0..12 Кб только на запись
Звук - бипер
Клавиатура - на основе ВВ55.

Орион
Процессор КР580ВМ80 2.5 Мгц
Видео 384x256 2/4/16 цветов
Основное ОЗУ 61 Кб
Дополнительное ОЗУ 60 Кб
Звук - бипер
Клавиатура - на основе ВВ55.

* в самой первой модели Специалиста цвета вообще не было. А в первой модели Ориона уже были все режимы.

При этом полной программной совместимости нет. Адрес ПЗУ/BIOS отличается C800->F800. Адрес видеопамяти отличается 9000->C000, хотя её формат совпадает. Адрес клавиатуры и расположение кнопок отличаются. Порт бипера отличается.

Но главное, отличается реализация цвета. У Специалиста есть регистр цвета, в который один раз мы заносим цвет и далее все нарисованное на экране будет этого цвета. В Орионе память цвета расположена во второй странице адресного пространства и программист должен вручную заносить туда данные. А код переключающий страницы должен размещаться по адресу F000-F7FF, это участок непереключаемой памяти.

В общем, поправить было не долго. Почти всё что пришлось сделать, это:

void bitBlt(uchar* d => de, uchar* s => hl, uint wh => bc) {
  asm {
bitBlt_l1:
    push d
    push b                        
bitBlt_l2:
    mov b, m   ; Читаем байт цвета
    inx h
    mvi a, 1   ; Дополнительное ОЗУ
    sta 0F900H
    mov a, b   ; Записываем байт цвета
    stax d
    xra a      ; Основное ОЗУ
    sta 0F900H
    mov a, m   ; Читаем байт пикселей
    inx h
    stax d     ; Записывает байт пикселей
    inx d      
    dcr c      ; Цикл по ширине
    jnz bitBlt_l2
    pop b
    pop d    
    inr d      ; Цикл по высоте
    dcr b
    jnz bitBlt_l1
  }
}


Единственное, мне пока лень писать собственный драйвер клавиатуры и я использую BIOS. А этот самый BIOS притормаживает анимацию, если пользователь нажал клавишу.

Все фото перемещены.

$
0
0
Отмыл, собрал и сфотографировал все свои компьютеры (что не отдал).

http://vk.com/albums-41468278

Получилось примерно 25 советских компов.

ГодНазваниеОЗУТип процессораВидео
1977Atari 26000,16502160x192 128
1983Микро 802-648080Т64x25 ч/б
1984Правец 8D486502240x200 8
1985БК001032КР1801ВМ1256x256 4
1985Yamaha КУВТ2256Z80256x212 256
1985Commodore C1281286502+Z80640x200 16
1986Львов ПК01648080200x225 4
1986Альфа БК168080Т64x25 ч/б
1987ZX Spectrum +3128Z80256x192 15
1987ПК8000 Сура648080256x192 16
1987Электроника МС 0511 (УКНЦ)1922xКР1801ВМ2640×288 128
1987БК0010.0132КР1801ВМ1256x256 4
1987Микроша328080Т64x25 ч/б
1987Партнер 01.01528080Т64x25 ч/б
1987Ассистент 1281288086320x200 4
1987Электроника КР0316/328080Т64x25 ч/б
1987Вектор 06Ц648080256x256 16
1988Поиск 1128/5128088320x200 4
1988Корвет ПК-8010113/2578080512x256 8
1988Микролаб КР580ИК8018080-
1988Апогей БК01Ц568080192x104 8
1988Специалист Линёвский488080384x256 8
1889Русский клон ZX Spectrum (Процессор)48Z80256x192 15
1989БК0011128КР1801ВМ1256x256 4
1990Криста-2648080256x256 16
1990Искра 1080 Тарту648080384x256 4
1990Орион 1281288080384x256 15
1990БК0011М128КР1801ВМ1256x256 4
1990Юниор ПК ФВ65061288080240x120 8
1990Специалист (Учебный Дисплейный Класс)488080384x256 ч/б
1990ПК8002 Эльф648080256x192 16
2013Специалист МХ25888080/Z80384x256 15
Viewing all 319 articles
Browse latest View live