1 (изменено: Freeman, 08.06.2020 в 01:42)

Тема: Модуль CRT

Выложил на GitHub модуль CRT с переписанными консольными примерами и новым примером ConsoleColors, адаптированный с форума. Esc-последовательности вроде правильно переписал, но программа какую-то ерунду выводит при установке некоторых цветов. Похоже на баг в самой консоли.

2 (изменено: Freeman, 08.06.2020 в 23:32)

Re: Модуль CRT

procedure ConsoleInit(Title: PKolibriChar);

лучше сделать overload.
Значение $FFFFFFFF вот здесь

 ConsoleInitProc($FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, Title);

означает — по умолчанию стандартный размер(80x25).
Вот из справки

void __stdcall con_init(dword wnd_width, dword wnd_height,
    dword scr_width, dword scr_height, const char* title);
Инициализация консоли. Вызывается один раз в начале программы.
wnd_width, wnd_height - высота и ширина (в символах) видимой в окне консоли
    области;
scr_width, scr_height - высота и ширина (в символах) всей консоли;
любые из первых 4 параметров могут быть установлены в -1 (=0xFFFFFFFF)
    - использовать значения по умолчанию;
title - заголовок окна консоли.

Возможность менять  размер бывает очень полезной.
Например, мне это пригодилось в программе CoffDump, под первым спойлером — стандартный размер, под вторым спойлером — увеличенный размер board.kolibrios.org/viewtopic.php?f=9&t=3577#p69106

Добавлено 2020-06-08 в 03:32

Раз уж беззнаковое 4-ёх байтовое имеет тип LongWord, то, думаю, для знаковых логичнее LongInt, а не Integer.

Добавлено 2020-06-08 в 03:40

kbhit — это KeyPressed, а не Escape

Добавлено 2020-06-08 в 03:44

pascal.net.ru/Procedures
wiki.freepascal.org/Crt

Добавлено 2020-06-08 в 03:48

Ну про Write писал уже там
А текущий вариант — потенциальный источник проблем

Добавлено 2020-06-08 в 04:07

WndWidth, WndHeight, ScrWidth, ScrHeight: LongInt

LongWord же, это ведь размеры, а не координаты!

Добавлено 2020-06-08 в 04:14

ошибки в TextBackground:

Light = #27'[1m';
#27'[37m'  // LightGray

должно быть

Light = #27'[5m';
#27'[47m'  // LightGray

Добавлено 2020-06-08 в 04:16

ошибки в TextColor

  Light = #27'[5m';

должно быть

Light = #27'[1m';

Добавлено 2020-06-08 в 05:01

Freeman пишет:

программа какую-то ерунду выводит при установке некоторых цветов

вот тут

    DarkGray..White:
      begin
        Write(Colors[color]);
        Write(Light);
      end;

надо делать "Color-8"

    DarkGray..White:
      begin
        Write(Colors[Color-8]);
        Write(Light);
      end;

3 (изменено: Freeman, 08.06.2020 в 21:14)

Re: Модуль CRT

НЕТ! Не надо ни каких overload версий! Лучше ОДНУ, НОРМАЛЬНУЮ:

procedure ConsoleInit(Title: PKolibriChar; WndWidth: LongInt = $FFFFFFFF; WndHeight: LongInt = $FFFFFFFF; ScrWidth: LongInt = $FFFFFFFF; ScrHeight: LongInt = $FFFFFFFF);

И var блоку процедур:

var
  ConsoleExit: procedure(CloseWindow: Boolean); stdcall;
  KeyPressed: function: Boolean;
  ReadKey: function: KolibriChar; stdcall;
  SetCursorHeight: function(Height: Integer): Integer; stdcall;
  Write: function(const Text: PKolibriChar): LongInt; cdecl varargs;
  WriteText: procedure(Text: PKolibriChar; Length: LongWord); stdcall;

НЕ МЕСТО в интерфейсной части модуля. Их лучше скрыть в implementation, что бы пользователь их не переопределил с дуру. Лучше сделать wrapper-ы этих процедур.

Добавлено 2020-06-08 в 17:39

Вот как выглядит модуль CRT в FreePascal. Думаю, "ванильный" Pascal, содержит тоже самое: https://wiki.freepascal.org/Crt

Желательно реализовать в Колибри как можно больше из того, что там есть. Что бы не переучивать пользователей. Ну и может что-то своё завести.

Модератор: тег надо оформлять тегом [ code ].

4 (изменено: Freeman, 10.06.2020 в 00:51)

Re: Модуль CRT

Я когда-то делал для Oberon-07 такой пример github.com/AntKrotov/oberon-07-compiler … arpet.ob07
Теперь решил переделать под Delphi, немного изменил его даже.

Для этого нужна ещё функция ReadString, я добавил её в CRT
  GetS:              Function(Buffer: PAnsiChar; Count: LongWord): PAnsiChar; stdcall;
.........................................................
function  ReadString(Buffer: PAnsiChar; Count: LongWord): PAnsiChar;
.........................................................
  function  ReadString(Buffer: PAnsiChar; Count: LongWord): PAnsiChar;
  begin
    Result := GetS(Buffer,  Count);
  end;  
.........................................................
    GetS        := GetProcAddress(hConsole, 'con_gets');
Сам пример
program SierpinskiCarpet;

uses
  CRT;

var
  Buf: array [0..2] of AnsiChar;
  Order: LongWord;

function StrToInt(const S: PChar): LongInt;
asm
        PUSH   ESI
        MOV    ESI, S
        XOR    EDX, EDX
        XOR    ECX, ECX
        XOR    EAX, EAX
        LODSB
        CMP    AL, '-'
        JNE    @nosign
        NOT    EDX
@next:
        LODSB
@nosign:
        SUB    AL, 48
        CMP    AL, 9
        JNBE   @done
        LEA    ECX, [ECX + ECX * 4]
        LEA    ECX, [EAX + ECX * 2]
        JMP    @next
@done:
        MOV    EAX, ECX
        ADD    EAX, EDX
        XOR    EAX, EDX
        POP    ESI
end;

function Pow(b, n: LongInt): LongInt;
var
   i: LongInt;
begin
  Result := 1;
  for i := 1 to n do
    Result := Result * b;
end;

function InCarpet(x, y: LongInt): Boolean;
var
  NeedExit: Boolean;
begin
  NeedExit := False;
  Result := True;
  while (x > 0) and (y > 0) and (NeedExit = False) do
  begin
    if (x mod 3 = 1) and (y mod 3 = 1) then
    begin
      Result := False;
      NeedExit := True;
    end;
    y := y div 3;
    x := x div 3;
  end;
end;

procedure PrintSierpinski(n: LongInt);
var
  i, j, l: LongInt;
begin
  l := Pow(3, n) - 1;
  for i := 0 to l do
  begin
    for j := 0 to l do
    begin
      if InCarpet(i, j) then
      begin
        TextBackground(Yellow);
        Write(' ');
      end
      else
      begin
        TextBackground(LightMagenta);
        Write(' ');
      end;
    end;
    WriteLn('');
  end;
end;

begin
  ConsoleInit($FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, 'Sierpinski Carpet');
  Write('Input carpet order(0..3):');
  ReadString(Buf, SizeOf(Buf));
  Order := StrToInt(Buf);
  PrintSierpinski(Order);
  ReadKey;
end.
результат

misc.php?action=pun_attachment&item=32&download=0

5 (изменено: Freeman, 08.06.2020 в 22:53)

Re: Модуль CRT

StrToInt - это часть модуля SysUtils

function Pow - вообще в Delphi есть функция Power, написана на ассемблере в модуле Math.

ReadString - заготовка для Read и ReadLn в модуль CRT.

Если так подумать, то правильный путь - возрождать оригинальные модули (или хотя бы их части). Как-то не очень хорошо, писать по второму разу то, что уже существует в Delphi.

Вот вам и простор для развития SDK. Если всё это дело правильно оформить - будет мощная вещь. SDK "из коробки" должна поставлять кучу возможностей. А иначе будим по 10 раз писать одни и те же вещи для разных примеров.

К стати на GitHUB до сих пор старые модули, хотя тут - их улучшенные версии с рабочими секциями инициализации и финализации.

Модератор: нужно пользоваться внутренней адресацией.

6

Re: Модуль CRT

Выложил на GitHub версию с перегруженными Write/WriteLn, использующими открытый массив для вызова printf. Скопировал реализацию из CoreLite, где таким же образом вызывается wvsprintf, но что-то не работает. Все примеры, использующие эту функцию, выводят нечто похожее на мусор. На отладку времени нет. Прошу разобраться, если можно.

7 (изменено: Freeman, 16.02.2021 в 01:26)

Re: Модуль CRT

Вот это может работать не так, как ожидается

procedure Delay(Milliseconds: LongWord);
begin
  Sleep(Milliseconds div 10);
end;

если Milliseconds < 10(после деления вообще 0 будет, но лигичнее сделать хотя бы 1).
Я в своём эмуляторе PELoad  board.kolibrios.org/viewtopic.php?f=9&t=2318 

решил эту проблему так
;**********************************************************************************
Sleep: ;///////////////////////////////////////////////////////////////////////////
;**********************************************************************************
%define dwMilliseconds        [esp +  4 +1*4] ; time interval
        push   ebx

        mov    eax, dwMilliseconds
        mov    ebx, 10
        cmp    eax, ebx
        jae    .ae
        add    eax, 10                     ; avoid zero result if dwMilliseconds < 10
.ae:
        xor    edx, edx
        div    ebx
        mov    ebx, eax

        mov    eax, 5
        int    64

        pop    ebx
        ret    4
%undef dwMilliseconds

да, похоже и там не совсем верно, так как, если изначально было ноль, то стаенет 1, а должно быть тоже 0.
Тогда, вместо

add    eax, 10

нужно делать

add    eax, 9
Freeman пишет:

перегруженными Write/WriteLn, использующими открытый массив для вызова printf. Скопировал реализацию из CoreLite, где таким же образом вызывается wvsprintf, но что-то не работает. Все примеры, использующие эту функцию, выводят нечто похожее на мусор.

Есть функция wvsprintf, а есть wsprintf, и это — не одно и тоже!
Функция printf из библиотеки Console похожа на wsprintf.

Дело в ассемблерном вызове printf из функции Write

Короче, попробуй тут

function Write(Format: PKolibriChar; const Args: array of const): Integer;
const
  VarArgSize = SizeOf(TVarRec);
asm
        PUSH EDI
        PUSH EBX
        MOV EBX, ESP

        INC ECX
        JZ @@call
@@arg:
        MOV EDI, [EDX + ECX * VarArgSize - VarArgSize]
        PUSH EDI
        LOOP @@arg
@@call:
        PUSH ESP
        PUSH EAX
        CALL PrintF

        MOV ESP, EBX
        POP EBX
        POP EDI
end;

убрать

PUSH ESP

под винду аналогично — можно без "PUSH ESP" использовать wsprintf вместо wvsprintf

Добавлено 2020-06-09 в 14:20

Вот здесь github.com/vapaamies/SDK/blob/master/Ex … lloGUI.dpr на самом деле находится консольный пример, и не обязательно делать

uses
KolibriOS

достаточно CRT.
Вот как тут github.com/vapaamies/SDK/blob/master/Ex … /Hello.dpr

github.com/vapaamies/KolibriOS/commit/c … d2a6049c9e

     -Write('%02x.%02x.%02x', Day, Month, Year);
     -Write(' - %02x:%02x:%02x', Hours, Minutes, Seconds);
     +Write('%02u.%02u.%02u', [Day, Month, Year]);
     +Write(' - %02u:%02u:%02u', [Hours, Minutes, Seconds]);

и по-твоему оно теперь правильно работает?
Читаем справку по спецификаторам формата функции printf, например, где-нибудь тут www.cplusplus.com/reference/cstdio/printf
Что за мания "исправлять" работающий код!?

Добавлено 2020-06-09 в 15:06

Вместо

@@arg:
        MOV EDI, [EDX + ECX * VarArgSize - VarArgSize]
        PUSH EDI
        LOOP @@arg

можно писать так

@@arg:
        PUSH dword ptr [EDX + ECX * VarArgSize - VarArgSize]
        LOOP @@arg

Тогда сохранять регистр edi(PUSH\POP) не требуется.
Почему, кстати, для сохранения esp используется ebx, а не ebp, как обычно? Хотя в данном случае, думаю, разницы большой нет.

8

Re: Модуль CRT

0CodErr пишет:

если Milliseconds < 10(после деления вообще 0 будет, но лигичнее сделать хотя бы 1).

Если делать по-паскалевски, надо реализовать Round и использовать деление с плавающей запятой.

0CodErr пишет:

Есть функция wvsprintf, а есть wsprintf, и это — не одно и тоже!

Я в 3 часа ночи писал, не разобрался.

0CodErr пишет:
     +Write('%02u.%02u.%02u', [Day, Month, Year]);
     +Write(' - %02u:%02u:%02u', [Hours, Minutes, Seconds]);

...
Что за мания "исправлять" работающий код!?

Тоже издержки ночной работы. Стал экспериментировать и забыл откатить. Исправлю в коммите с работающей printf.

0CodErr пишет:

Почему, кстати, для сохранения esp используется ebx, а не ebp, как обычно?

EBP используется Delphi в прологах-эпилогах функций. Хотя надо смотреть подстрочник, может в этом случае и не используется... А, в CoreLite у функции есть третий параметр, так что используется. Или я чего-то не помню...

9

Re: Модуль CRT

Freeman пишет:
0CodErr пишет:

если Milliseconds < 10(после деления вообще 0 будет, но лигичнее сделать хотя бы 1).

Если делать по-паскалевски, надо реализовать Round и использовать деление с плавающей запятой.

Это делается примерно так

(Number + (Divider div 2 )) div Divider

в нашем случае

 Divider = 10
(Divider div 2 ) = 5

Привожу пример такого перевода миллисекунд в сотые доли секунды:

(20 + 5) div 10 = 2
(21 + 5) div 10 = 2
(22 + 5) div 10 = 2
(23 + 5) div 10 = 2
(24 + 5) div 10 = 2
(25 + 5) div 10 = 3
(26 + 5) div 10 = 3
(27 + 5) div 10 = 3
(28 + 5) div 10 = 3
(29 + 5) div 10 = 3

и никакой плавающей запятой не нужно!

10

Re: Модуль CRT

0CodErr пишет:
Сам пример
program SierpinskiCarpet;

uses
  CRT;

var
  Buf: array [0..2] of AnsiChar;
  Order: LongWord;

function StrToInt(const S: PChar): LongInt;
asm
        PUSH   ESI
        MOV    ESI, S
        XOR    EDX, EDX
        XOR    ECX, ECX
        XOR    EAX, EAX
        LODSB
        CMP    AL, '-'
        JNE    @nosign
        NOT    EDX
@next:
        LODSB
@nosign:
        SUB    AL, 48
        CMP    AL, 9
        JNBE   @done
        LEA    ECX, [ECX + ECX * 4]
        LEA    ECX, [EAX + ECX * 2]
        JMP    @next
@done:
        MOV    EAX, ECX
        ADD    EAX, EDX
        XOR    EAX, EDX
        POP    ESI
end;

function Pow(b, n: LongInt): LongInt;
var
   i: LongInt;
begin
  Result := 1;
  for i := 1 to n do
    Result := Result * b;
end;

function InCarpet(x, y: LongInt): Boolean;
var
  NeedExit: Boolean;
begin
  NeedExit := False;
  Result := True;
  while (x > 0) and (y > 0) and (NeedExit = False) do
  begin
    if (x mod 3 = 1) and (y mod 3 = 1) then
    begin
      Result := False;
      NeedExit := True;
    end;
    y := y div 3;
    x := x div 3;
  end;
end;

procedure PrintSierpinski(n: LongInt);
var
  i, j, l: LongInt;
begin
  l := Pow(3, n) - 1;
  for i := 0 to l do
  begin
    for j := 0 to l do
    begin
      if InCarpet(i, j) then
      begin
        TextBackground(Yellow);
        Write(' ');
      end
      else
      begin
        TextBackground(LightMagenta);
        Write(' ');
      end;
    end;
    WriteLn('');
  end;
end;

begin
  ConsoleInit($FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, 'Sierpinski Carpet');
  Write('Input carpet order(0..3):');
  ReadString(Buf, SizeOf(Buf));
  Order := StrToInt(Buf);
  PrintSierpinski(Order);
  ReadKey;
end.

А можно адаптировать StrToInt к ShortString? Начнем делать SysUtils. Наверное, лучше так:

function StrToInt(const Str: ShortString): LongInt;

Тогда можно будет добавить ReadLn и сам пример.