3179

Программа с равномерным выполнением задач

Курсовая

Информатика, кибернетика и программирование

Два потока: первый читает информацию из файла (например, стихи или текст программы) в буфер, второй эту информацию из буфера выдает на экран. При заполнении окна вывода до конца его содержимое.

Русский

2012-10-26

145 KB

17 чел.

Задание.

Необходимо написать программу, реализующую параллельную работу нескольких процессов. Каждый процесс может состоять из одного или нескольких потоков. Любой из потоков, работающих в составе этих процессов, может быть приостановлен и вновь запущен некоторой определенной клавишей (можно использовать буквенные или цифровые клавиши). Нажатия клавиш обрабатывать с помощью прерывания от клавиатуры (по материалам лаб. работы №1).

Окончание работы программы должно происходить при приостановке всех потоков их ключевыми клавишами либо при нажатии клавиши ESC. При окончании работы необходимо выполнить корректное завершение, т.е. “дочитать” всю информацию из буфера каждого процесса (при его наличии), закрыть все открытые файлы и т.п. – по материалам лаб. работы №4.

Те потоки, которые выводят информацию на экран, должны использовать для этого каждый свое отдельное окно, обрамленное рамкой – экран визуально делится на несколько окон, в каждом из которых отображается один процесс.

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

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

Задачи 1, 3, 6 предполагают наличие управляющего блока, который, используя прерывания таймера, случайным образом определяет очередной активный поток и выделяет ему кванты времени (возможно, в рамках большего кванта, выделенного всему процессу). Кванты времени – как для процесса в целом, так и для его потоков – изначально задавать некоторой фиксированной величины, но предусмотреть возможность её изменения во время работы путем нажатия некоторых ключевых клавиш (для каждого из потоков предусмотреть свою клавишу) – т.е. можно, например, увеличить или уменьшить квант только потока-производителя. При этом допустимы разные варианты реализации – “общий” квант может либо меняться, либо оставаться постоянным (тогда при ускорении одного потока другой автоматически замедлится, т.к. величина его кванта уменьшится).

Потоки этого класса задач могут иметь три статуса: “активен”, “ожидает” или “приостановлен”. В процессе работы может возникнуть, например, следующая ситуация. Поток-потребитель приостановлен своей ключевой клавишей, следовательно, буфер не освобождается. Поток-производитель активен, он заполнит буфер информацией и перейдёт в состояние ожидания. Из этого состояния он сможет выйти только после того, как будет возобновлена (нажатием клавиши) работа потока-потребителя, который освободит место в буфере для помещения новой информации. В случае приостановки производителя возникнет аналогичная ситуация, только с пустым буфером. Приостановка/возобновление потока возможны в любом его состоянии – как в активном, так и в состоянии ожидания.

Задачи 2, 4, 5, 7 должны выполняться равномерно, независимо от степени загрузки системы. Для этого каждой из них необходимо получать управление через фиксированное количество “тиков” системного таймера, во время которого они выполнят какое-то свое элементарное действие (“бегущая строка” или “летающий объект” сместится на одну позицию, сменится нота в музыке…). При такой реализации скорость каждого потока будет определяться количеством “тиков” таймера между его запусками. Для уменьшения скорости такого потока достаточно после нажатия ключевой клавиши предоставлять ему управление реже, через большее число “тиков”, соответственно для ускорения такого потока – опять же после нажатия ключевой клавиши – ему предоставляется управление чаще, в пределе – на каждом “тике”. Так, “бегущая строка” должна двигаться равномерно с постоянной скоростью (если она не приостановлена ключевой клавишей) независимо от количества активных процессов в системе, музыка – тоже играть равномерно… Потоки этого класса задач могут иметь два статуса: “активен” и “приостановлен”.

Задания:

1. Два потока: первый читает информацию из файла (например, стихи или текст программы) в буфер, второй эту информацию из буфера выдаёт на экран. При заполнении окна вывода до конца его содержимое не должно обновляться полностью – вывод новой информации должен осуществляться в последнюю строку, а все остальные строки смещаться вверх (по материалам лаб. работы №4). Имя читаемого файла задавать как параметр командной строки. После окончания файла он начинает считываться заново.

5. Вывод заголовка работы (фамилия и имя автора и название работы) в виде “бегущей строки” (по материалам лаб. работы №5). Место вывода (верхняя или нижняя строка экрана) задавать параметром командной строки.

7. Движущийся объект (в простейшем случае, например, летающий и отражающийся от границ окна шарик). Движение может осуществляться свободно или при управлении с клавиатуры (по материалам лаб. работы №5).

8. В углу экрана с заданными в качестве параметров координатами поместить часы, показания которых должны соответствовать системному времени. Обновлять показания часов каждую секунду (по материалам лаб. работы №3).


Параметры командной строки.

Программа принимает четыре параметра – имя файла с текстом, координаты часов (x, y, причём 1 ≤ x ≤ 73, 1 ≤ y ≤ 25) и положение бегущей строки (a – вверху, b – внизу)  Пример:

solution.exe file.txt 1 1 a

Описание основных алгоритмов, используемых в программе.

В программе три фоновых процесса:

  1.  Часы. Время рассчитывается с помощью прерываний системного таймера; изначально при запуске программы получается системное время и от него идёт самостоятельный отсчёт. Координаты часов задаются параметрами командной строки.
  2.  Бегущая строка. Движется строка в верхней или нижней строке экрана (задаётся параметром командной строки). Частота сдвига строки регулируется с помощью прерываний таймера.
  3.  Движущийся объект (в данной работе – квадрат). Движется в области описания клавиш управления. Частота сдвига также регулируется с помощью прерываний таймера.

Потоки производитель и потребитель на работу получают время, в течение которого они выполняют необходимые действия. Для обмена информацией между потоками используется FIFO-буфер. Уровень заполненности буфера отображается на экране в виде Progress Bar’а. Пользователь может добавить к выделенному времени ещё дополнительное время для каждого потока с помощью ключевых клавиш.

Описание основных переменных, констант и типов.

Типы.

Status – описывает состояние процесса в конкретный момент времени;

Video – используется для прямого доступа к видеопамяти.

Константы.

Line – текст бегущей строки;

MaxBufSize – размер буфера;

MaxStrLen – длина строки окна вывода;

StrSpeed – скорость бегущей строки;

SquareRefresh – частота обновления позиции движущегося объекта;

Delay1 – задержка для процесса производитель-потребитель.

Переменные.

sCl, sStr, sObj, sProd, sCons – состояния потоков;

tProd, tCons – дополнительные времена для потоков;

tCl, tStr, tObj – счётчики времени таймера;

Buf – буфер;

Top – вершина буфера;

OldTimerHandler, OldKeyboardHandler – старые обработчики прерываний;

SX, SY – координаты квадрата;

dx, dy – "направляющие" квадрата;

tmp_symbol, tmp_attr – перекрытая квадратом часть экрана;

T – счётчик времени таймера;

Len – длина бегущей строки;

Tmp – часть строки, перекрытая бегущей строкой;

CurLineN – номер строки вывода потока-потребителя;

CurPos – текущая позиция бегущей строки;

StrNum – номер строки экрана, в которой движется бегущая строка;

Scr – для прямого доступа к видеопамяти;

F – файл с текстом;

__Init, __End – флаги запуска и завершения;

CurThreadномер текущего потока;

Second, Minute, Hourвремя;

cnt, cnt_d – счетчики прерываний для часов;

RunSec, CorrectSecond – счетчик прерываний с момента запуска и корректирующая секунда;

discr – частота обновления показаний часов;

XCl, YCl – координаты часов.

Основные подпрограммы.

Print – вывод информации на экран с помощью прямого доступа к видеопамяти;

TimerHandler – обработчик прерываний системного таймера;

KeyboardHandler – обработчик прерываний клавиатуры;

Prod, Cons – потоки производитель и потребитель первого процесса;

Flyсдвиг летающего объекта;

TimeRefresh – получение системного времени и инициализация счётчиков;

ProgressBarRefresh – обновление показаний заполненности буфера на экране;

FillScreen – процедура заполнения экрана начальными сведениями и окнами;

Initialize – инициализация переменных перед запуском основных процедур;

Close – процедура завершения работы программы;

SetCurSizeизменение размера курсора.


Снимки экрана в различных ситуациях.

Неверное задание параметров командной строки.

Работа программы.

Исходный код программы.

program solution;

uses

 Crt, Dos;

const

 Line: String[80] = 'Курсовая работа.'; { Текст бегущей строки  }

 MaxBufSize = 30;                   { Размер буфера             }

 MaxStrLen = 37;                    { Длина строки окна вывода  }

 StrSpeed: Integer = 15;            { Скорость бегущей строки   }

 SquareRefresh = 2; { Частота обновления позиции движущегося объекта  }

 Delay1 = 10000;    { Задержка для процесса производитель-потребитель }

type

 Status = (Active, Waits, Suspended);  { Состояния процессов }

 Video = Array[1..25, 1..80] of Record { Массив для прямого доступа к видеопамяти }

   Symbol: Char;

   Attr: Byte;

 end;

var

 sCl, sStr, sObj, sProd, sCons: Status; { Состояния потоков }

 tProd, tCons: Word;   { Дополнительные времена для потоков }

 tCl, tStr, tObj: Word;   { Счётчики времени таймера        }

 Buf: Array[1..MaxBufSize] of String[MaxStrLen]; { Буфер }

 Top: Word;                       { Вершина буфера }

 OldTimerHandler, OldKeyboardHandler: Procedure; { Старые обработчики прерываний }

 SX, SY: Byte;    { Координаты квадрата     }

 dx, dy: Integer; { "Направляющие" квадрата }

 tmp_symbol: Array[0..1, 0..1] of Char; { Перекрытая квадратом часть экрана }

 tmp_attr: Array[0..1, 0..1] of Byte;

 T: Word;                       { Счётчик времени таймера }

 Len: Byte;                     { Длина бегущей строки    }

 tmp: String[40];               { Часть строки, перекрытая бегущей строкой }

 CurLineN: Byte;                { Номер строки вывода потока-потребителя   }

 CurPos: Integer;               { Текущая позиция бегущей строки }

 StrNum: Integer;  { Номер строки экрана, в которой движется бегущая строка }

 Scr: Video Absolute $B800:$0000; { Для прямого доступа к видеопамяти }

 F: Text;                         { Файл с текстом    }

 __Init, __End: Boolean; { Флаги запуска и завершения }

 CurThread: Byte;        { Номер текущего потока      }

 Second, Minute, Hour: Word; { Время }

 cnt, cnt_d: Word;           { Счетчики прерываний для часов }

 RunSec, CorrectSecond: Byte; { Счетчик прерываний с момента запуска и корректирующая секунда }

 discr: Integer;              { Частота обновления показаний часов }

 XCl, YCl: Byte;             { Координаты часов }

{ Вывод строки S на экран начиная с X-го столбца, Y-й строки цветом Color }

procedure Print(const S: String; const X, Y: Byte; const Color: Byte);

var

 i: Integer;

begin

 for i := X to X + Length(S) - 1 do begin

   Scr[Y, i].Attr := Color;

   Scr[Y, i].Symbol := S[i - X + 1];

 end;

end;

{ Разложение времени }

procedure CheckTime;

begin

 Inc(Minute, Second div 60);

 Second := Second mod 60;

 Inc(Hour, Minute div 60);

 Minute := Minute mod 60;

 Hour := Hour mod 24;

end;

{ Получает системное время и начинает собственный отсчет с помощью прерываний

 таймера }

procedure TimeRefresh;

var

 CS100: Word;

begin

 RunSec := 0;

 GetTime(Hour, Minute, Second, CS100);

 discr := 1;

 cnt_d := discr;

 CorrectSecond := 5;

 cnt := Trunc((99 - CS100) * 0.18);

end;

{ Добавляет нули слева для времени при необходимости }

function LeadingZero(W: Byte): String;

var

 S: String;

begin

 Str(W:0, S);

 if Length(S) = 1 then S := '0' + S;

 LeadingZero := S;

end;

{ Сдвигает летающий квадрат }

procedure Fly;

var

 i, j: Integer;

begin

 while (SX + dx > 61) or (SX + dx < 45) or (SY + dy > 22) or (SY + dy < 3) do begin

   case Random(4) of

     0: dx := -1;

     1: dx := 1;

     2: dx := 2;

     3: dx := -2;

   end;

   case Random(4) of

     0: dy := -1;

     1: dy := 1;

     2: dy := 2;

     3: dy := -2;

   end;

 end;

 for i := 0 to 1 do

   for j := 0 to 1 do begin

     Scr[SY + j, SX + i].Attr := tmp_attr[j, i];

     Scr[SY + j, SX + i].Symbol := tmp_symbol[j, i];

   end;

 SX := SX + dx;

 SY := SY + dy;

 for i := 0 to 1 do

   for j := 0 to 1 do begin

     tmp_attr[j, i] := Scr[SY + j, SX + i].Attr;

     tmp_symbol[j, i] := Scr[SY + j, SX + i].Symbol;

   end;

 for i := 0 to 1 do

   for j := 0 to 1 do begin

     Scr[SY + j, SX + i].Attr := LightRed;

     Scr[SY + j, SX + i].Symbol := Chr(219);

   end;

end;

{ Обработчик прерываний таймера }

{$F+}

procedure TimerHandler; Interrupt;

var

 i, j: Integer;

begin

 Inc(T);

 if sCl = Active then begin

   if cnt = 0 then

   begin

     Inc(Second);

     CheckTime;

     Inc(RunSec);

     Inc(cnt_d);

     Dec(CorrectSecond);

     if CorrectSecond = 0 then

     begin

       CorrectSecond:=5;

       cnt := 19;

     end

     else

       cnt := 18;

     if cnt_d >= discr then

     begin

       cnt_d := 0;

       Print(LeadingZero(Hour) + ':' + LeadingZero(Minute) +

         ':' + LeadingZero(Second), XCl, YCl, White);

     end;

     if RunSec mod 20 = 0 then

     begin

       Inc(Second);

       CheckTime;

       if (20 mod discr = 0) or (discr mod 20 = 0) then

         Print(LeadingZero(Hour) + ':' + LeadingZero(Minute) +

           ':' + LeadingZero(Second), XCl, YCl, White);

     end;

     if RunSec = 100 then

     begin

       RunSec := 0;

       Inc(Second);

       CheckTime;

       if (20 mod discr = 0) or (discr mod 20 = 0) then

         Print(LeadingZero(Hour) + ':' + LeadingZero(Minute) +

           ':' + LeadingZero(Second), XCl, YCl, White);

     end;

   end

   else

     Dec(cnt);

 end;

 if sStr = Active then begin { Бегущая строка }

   Inc(tStr);

   if tStr > 18 div StrSpeed then begin

     Dec(CurPos);

     if not __Init then begin

       Scr[StrNum, CurPos + Len].Attr := White;

       Scr[StrNum, CurPos + Len].Symbol := tmp[Length(tmp)];

     end;

     Delete(tmp, Length(tmp), 1);

     if (CurPos >= 1) and (CurPos + Len <= 80) then

       tmp := Scr[StrNum, CurPos].Symbol + tmp

     else

       tmp := Scr[StrNum, 80 + CurPos].Symbol + tmp;

     if (CurPos >= 1) and (CurPos + Len <= 80) then

       for i := CurPos to CurPos + Len - 1 do begin

         Scr[StrNum, i].Attr := Yellow;

         Scr[StrNum, i].Symbol := Line[i - CurPos + 1];

       end;

     if CurPos < 1 then begin

       if not __Init then { Если программа только запущена, то левую часть строки не отображаем }

         for i := 1 to Len + CurPos - 1  do begin { Отображаем то, что слева }

           Scr[StrNum, i].Attr := Yellow;

           Scr[StrNum, i].Symbol := Line[-CurPos + i + 1];

         end;

       for i := 80 + CurPos to 80 do begin { Отображаем то, что справа }

         Scr[StrNum, i].Attr := Yellow;

         Scr[StrNum, i].Symbol := Line[i - (80 + CurPos) + 1];

       end;

       if CurPos - 1 = -Len then begin

         CurPos := 80 - Len + 1;

         if __Init then __Init := False;

       end;

     end;

     tStr := 0;

   end;

 end;

 if sObj = Active then begin

   Inc(tObj);

   if tObj >= SquareRefresh then begin

     Fly;

     tObj := 0;

   end;

 end;

 Inline($9C);

 OldTimerHandler;

end;

{$F-}

{ Обновление показаний заполненности буфера }

procedure ProgressBarRefresh;

var

 BufferProgressBar: String[36];

begin

 BufferProgressBar := '';

 while Length(BufferProgressBar) < 36 * Top div MaxBufSize do

   BufferProgressBar := BufferProgressBar + Chr(219);

 while Length(BufferProgressBar) < 36 do

   BufferProgressBar := BufferProgressBar + Chr(176);

 Print(BufferProgressBar, 43, 13, White);

end;

{ Поток-производитель }

procedure Prod(const TMax: Integer);

var

 i: Integer;

 c: Char;

 S: String[MaxStrLen];

begin

 if (sProd = Waits) and (Top <> MaxBufSize) then sProd := Active;

 case sProd of

   Active:    Print('Активен    ', 64, 10, LightGreen);

   Waits:     Print('Ожидание   ', 64, 10, Yellow);

   Suspended: Print('Остановлен ', 64, 10, LightRed);

 end;

 T := 0;

 while (T <= TMax + tProd) and (sProd <> Suspended) do begin

   if Top = MaxBufSize then begin

     sProd := Waits;

     Print('Ожидание   ' , 64, 10, Yellow);

     Break;

   end;

   i := 0;

   S := '';

   while not EOF(F) and (i < MaxStrLen) do begin

     Read(F, c);

     if c = #13 then Break;

     if c = #10 then Continue;

     S := S + c;

     Inc(i);

   end;

   while Length(S) <> MaxStrLen do

     S := S + ' ';

   if EOF(F) then Reset(F);

   Inc(Top);

   Buf[Top] := S;

   ProgressBarRefresh;

   Delay(Delay1);

 end;

 if sProd = Active then Print('Время вышло' , 64, 10, LightGray);

end;

{ Поток-потребитель }

procedure Cons(const TMax: Integer);

var

 i, j: Integer;

begin

 if (sCons = Waits) and (Top <> 0) then sCons := Active;

 case sCons of

   Active:    Print('Активен    ', 64, 11, LightGreen);

   Waits:     Print('Ожидание   ', 64, 11, Yellow);

   Suspended: Print('Остановлен ', 64, 11, LightRed);

 end;

 T := 0;

 while (T <= TMax + tCons) and (sCons <> Suspended) do begin

   if Top = 0 then begin

     sCons := Waits;

     Print('Ожидание   ' , 64, 11, Yellow);

     Break;

   end;

   if CurLineN = 23 then

     for i := 3 to 22 do

       for j := 3 to 39 do

         Scr[i, j].Symbol := Scr[i + 1, j].Symbol

   else Inc(CurLineN);

   Print(Buf[1], 3, CurLineN, White);

   Dec(Top);

   for i := 1 to Top do

     Buf[i] := Buf[i + 1];

   ProgressBarRefresh;

   Delay(Delay1);

 end;

 if sCons = Active then Print('Время вышло', 64, 11, LightGray);

end;

{ Обработчик прерываний клавиатуры }

{$F+}

procedure KeyboardHandler; Interrupt;

var

 Z: String[5];

begin

 case Port[$60] of

   1:  __End := True;                   { нажата Esc }

   2:  if tProd > 0  then Dec(tProd);   { нажата 1 }

   3:  if tProd < 40 then Inc(tProd);   {        2 }

   4:  if tCons > 0  then Dec(tCons);   {        3 }

   5:  if tCons < 40 then Inc(tCons);   {        4 }

   51: if StrSpeed < 16 then Inc(StrSpeed);  { нажата < }

   52: if StrSpeed > 1  then Dec(StrSpeed);  {        > }

   59:   { нажата F1 }

     if sCl <> Suspended then begin

       sCl := Suspended;

       Print('Остановлен ', 64, 5, LightRed);

     end

     else begin

       TimeRefresh;

       sCl := Active;

       Print('Активен    ', 64, 5, LightGreen);

     end;

   60:  { F2 }

     if sStr = Active then begin

       sStr := Suspended;

       Print('Остановлен ', 64, 6, LightRed);

     end

     else begin

       Print('Активен    ', 64, 6, LightGreen);

       sStr := Active;

     end;

   61:  { F3 }

     if sObj = Active then begin

       sObj := Suspended;

       Print('Остановлен ', 64, 7, LightRed);

     end

     else begin

       Print('Активен    ', 64, 7, LightGreen);

       sObj := Active;

     end;

   62:  { F4 }

     if sProd <> Suspended then begin

       sProd := Suspended;

       Print('Остановлен ', 64, 10, LightRed);

     end

     else begin

       sProd := Active;

       if CurThread = 0 then Print('Активен    ', 64, 10, LightGreen)

       else Print('Ожидание   ', 64, 10, Yellow);

     end;

   63:  { F5 }

     if sCons <> Suspended then begin

       sCons := Suspended;

       Print('Остановлен ', 64, 11, LightRed)

     end

     else begin

       sCons := Active;

       if CurThread = 1 then Print('Активен    ', 64, 11, LightGreen)

       else Print('Ожидание   ', 64, 11, Yellow);

     end;

 end;

 Print('      ', 64, 16, White);

 Print('      ', 64, 17, White);

 Str(tProd, Z);

 Print(Z, 64, 16, White);

 Str(tCons, Z);

 Print(Z, 64, 17, White);

 Inline($9C);

 OldKeyboardHandler;

end;

{$F-}

{ Изменение размера курсора }

procedure SetCurSize(const B, E: Byte);

var

 R: Registers;

begin

 with R do begin

   AH := $01;

   CH := B;

   CL := E;

 end;

 Intr($10, R);

end;

{ Действия при завершении работы программы }

procedure Close;

begin

 sProd := Suspended;

 sCons := Active;

 T := High(T);

 CurThread := 1;

 while Top <> 0 do begin

   T := 0;

   Cons(1 + Random(10));

 end;

 SetIntVec($9, Addr(OldKeyboardHandler));

 SetIntVec($8, Addr(OldTimerHandler));

 Window(1, 1, 80, 25);

 GotoXY(1, 25);

 SetCurSize($06, $07);

 Halt(0);

end;

{ Заполнение экрана начальными данными }

procedure FillScreen;

begin

 TextBackground(Black);

 ClrScr;

 Window(2, 2, 40, 24);

 TextBackground(White);

 ClrScr;

 TextColor(Black);

 GotoXY(11, 1);

 Write('Поток-потребитель');

 Window(3, 3, 39, 23);

 TextBackground(Black);

 ClrScr;

 Window(42, 2, 79, 24);

 TextBackground(White);

 ClrScr;

 GotoXY(11, 1);

 Write('Панель управления');

 Window(43, 3, 78, 23);

 TextBackground(Black);

 ClrScr;

 Window(43, 3, 78, 4);

 TextBackground(3);

 ClrScr;

 GotoXY(9, 1);

 TextColor(Yellow);

 WriteLn('Изменение состояний');

 GotoXY(10, 2);

 Write('фоновых процессов');

 Window(43, 5, 78, 7);

 TextBackground(Black);

 ClrScr;

 TextColor(White);

 WriteLn('F1 Часы            :');

 WriteLn('F2 Бегущая строка  :');

 Write('F3 Летающий объект :');

 Window(43, 8, 78, 9);

 TextBackground(3);

 ClrScr;

 TextColor(LightRed);

 GotoXY(7, 1);

 WriteLn('Производитель-потребитель');

 GotoXY(10, 2);

 TextColor(Yellow);

 Write('Изменение состояний');

 Window(43, 10, 78, 11);

 TextBackground(Black);

 ClrScr;

 TextColor(White);

 WriteLn('F4 Производитель   :');

 Write('F5 Потребитель     :');

 Window(43, 12, 78, 12);

 TextBackground(3);

 ClrScr;

 TextColor(Yellow);

 GotoXY(5, 1);

 Write('Уровень заполненности буфера');

 Window(43, 14, 78, 15);

 ClrScr;

 GotoXY(11, 1);

 WriteLn('Добавленное время');

 GotoXY(8, 2);

 Write('<уменьшить> / <добавить>');

 Window(43, 16, 78, 17);

 TextBackground(Black);

 ClrScr;

 TextColor(White);

 WriteLn('1 / 2 Производитель:');

 Write('3 / 4 Потребитель  :');

 Window(43, 18, 78, 18);

 TextBackground(3);

 ClrScr;

 TextColor(LightRed);

 GotoXY(12, 1);

 Write('Другие команды');

 Window(43, 19, 78, 23);

 TextBackground(Black);

 ClrScr;

 TextColor(White);

 WriteLn('> / < - скорость бегущей строки');

 Write('Esc - выход');

end;

{ Инициализация основных переменных }

procedure Initialize;

var

 i, j: Integer;

begin

 SetCurSize($20, $00);

 sCl := Active;

 sStr := Active;

 sObj := Active;

 sProd := Active;

 sCons := Active;

 T := 0;

 tCl := 0;

 tStr := 0;

 tObj := 0;

 Print('Активен   ', 64, 5, LightGreen);

 Print('Активен   ', 64, 6, LightGreen);

 Print('Активен   ', 64, 7, LightGreen);

 Print('Не запущен', 64, 10, LightGray);

 Print('Не запущен', 64, 11, LightGray);

 Print('0', 64, 16, White);

 Print('0', 64, 17, White);

 ProgressBarRefresh;

 TimeRefresh;

 Len := Length(Line);

 CurLineN := 2;

 CurPos := 1;

 __Init := True;

 __End := False;

 tmp := '';

 for i := 1 to Len do

   tmp := tmp + Scr[StrNum, i].Symbol;

 dx := -1;

 dy := -1;

 SX := 50;

 SY := 22;

 for i := 0 to 1 do

   for j := 0 to 1 do begin

     tmp_attr[j, i] := Scr[SY + j, SX + i].Attr;

     tmp_symbol[j, i] := Scr[SY + j, SX + i].Symbol;

   end;

 GetIntVec($9, @OldKeyboardHandler);

 SetIntVec($9, Addr(KeyboardHandler));

 GetIntVec($8, @OldTimerHandler);

 SetIntVec($8, Addr(TimerHandler));

end;

var

 i, j: Integer;

 p: String[1];

begin

 if ParamCount < 4 then begin

   WriteLn('Неправильное число параметров.');

   WriteLn;

   WriteLn('Формат параметров:');

   WriteLn('  имя_файла F X Y P');

   WriteLn('где');

   WriteLn('F - имя файла с текстом');

   WriteLn('X, Y - координаты часов (1 <= X <= 73, 1 <= Y <= 25)');

   WriteLn('P - позиция бегущей строки: a - вверху, b - внизу');

   ReadLn;

   Halt;

 end;

 Assign(F, ParamStr(1));

 {$I-}

 Reset(F);

 {$I+}

 if IOResult <> 0 then begin

    WriteLn('Не удаётся открыть указанный файл.');

    ReadLn;

    Halt;

 end;

 Val(ParamStr(2), XCl, i);

 Val(ParamStr(3), YCl, j);

 p := ParamStr(4);

 if (i <> 0) or (j <> 0) or (XCl < 1) or (XCl > 73) or (YCl < 1) or (YCl > 25) or not (Upcase(p[1]) in ['A', 'B']) then begin

   WriteLn('Неверно заданы параметры.');

   WriteLn;

   WriteLn('Формат параметров:');

   WriteLn('  имя_файла F X Y P');

   WriteLn('где');

   WriteLn('F - имя файла с текстом');

   WriteLn('X, Y - координаты часов (1 <= X <= 73, 1 <= Y <= 25)');

   WriteLn('P - позиция бегущей строки: a - вверху, b - внизу');

   ReadLn;

   Halt;

 end;

 if Upcase(p[1]) = 'A' then StrNum := 1 else StrNum := 25;

 FillScreen;

 Initialize;

 Randomize;

 while True do begin

   if __End then Close;

   CurThread := Random(2);

   case CurThread of

     0: Prod(10 + Random(10));

     1: Cons(10 + Random(10));

   end;

 end;

end.