ZX-Ревю 1991 №7-8 1990 г.

Профессиональный подход - Продолжая печатать рекомендации известного британского программиста Стива Тернера для тех, кто делает первые шаги в написании больших и сложных программ в машинных кодах, мы поговорим о структурном программировании.


Профессиональный подход

(Продолжение)

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

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

Я вижу следующие преимущества в структурном программировании:

1. Огромная неохватная задача разбивается на несколько простых подзадач.

2. Этот метод дисциплинирует образ мышления и порядок программирования. Каждая процедура выполняет свою частную, отдельную задачу.

3. Разные процедуры нередко имеют сходные черты, что очень упрощает программирование.

4. Значительно упрощается процесс отладки, т.к. снижается количество ошибок.

Примечание "ИНФОРКОМА"

Стив Тернер не привел на наш взгляд самого главного достоинства структурного программирования - возможности организации коллективного программирования, а в наши дни 95% программ создаются коллективно.

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

Более того, достижения одного сразу становятся достижениями всех членов команды и прогресс в разработке быстро нарастает.

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

Элементы структурированной программы.

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

1. Последовательные команды.

Одна команда идет за другой.

Компьютер выполняет свои команды последовательно. На рис. 1 это показано на структурной диаграмме.

Каждый квадрат представляет отдельную задачу.

Последовательность их исполнения - слева направо, в отличие от блок-схем, в

Рис.1

2. Условное ветвление.

Если <условие> исполнить <задача>

IF < условие > DO < задача>

Все программные языки имеют средства для того, чтобы исполнять какую-либо задачу только если это необходимо. В языках высокого уровня операторы могут выполняться или не выполняться в зависимости от какого-либо условия. В языках низкого уровня (например в АССЕМБЛЕРе) есть инструкции, после исполнения которых определяется, какая инструкция будет выполняться следующей. Это переходы (JP и JR), вызовы (CALL) и возвраты (RET). Рекомендуется всячески избегать излишних переходов и, если использовать переходы, так только условные, например JR NC, JR Z,... и т.п.

На рис.2 представлен элементарный блок "IF" (если).

X

if

условие задача D

Рис.2

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

Рис.3

Здесь представлено условие IF, что делать, если оно выполняется (задача E) и что делать в ином случае - ELSE (задача F).

3. Циклы (итерации).

Они позволяют выполнить задачу многократно, пока соблюдается какое-то условие.

DO <задача> UNTIL <условие>

Задача может быть выполнена столько раз, сколько это необходимо. В Бейсике этим занимаются операторы FOR...NEXT. На АССЕМБЛЕРе это можно сделать двумя командами перехода. Одна ставится в конце задачи и выполняет переход к началу, а вторая - условие выхода из образовавшейся петли. Если первый переход может и не быть условным, то уж второй - обязательно условный. Часто, когда я пишу программу, ставлю условие выхода последней командой в цикле. В этом случае можно обойтись одним переходом вместо двух. Тогда программа автоматически "вываливается" из цикла, когда выходное условие перехода перестает выполняться. По этому же принципу работает и команда АССЕМБЛЕРа DJNZ.

На рис. 4 показан элементарный цикл.

X

loop UNTIL условие задача G

Рис.4

Работая с циклами, я всегда выполняю дополнительные операции:

1). Инициализация переменных перед циклом. Установка исходных значений в регистрах и т.п.

2). Программирование цикла.

3). Финишные операции после завершения цикла.

Рис.5

Объединение элементов.

Рассмотрим как объединяются эти три основные типа структурных элементов в единое целое на примере задачи о чашке кофе. На рис. 6 показана последовательность действии при выполнении этой задачи. (Судя по схеме, Стив Тернер пьет растворимый кофе или просто не хочет раскрывать глубоких секретов приготовления кофе натурального -Прим. "Инфоркома").

В правом верхнем углу блоков с условиями типа IF поставлен символ "O", а в блоках с циклами символ "*".

Предположим, что нам надо запрограммировать кухонного робота на выполнение этой задачи. Если он уже это делал, то нам достаточно одного верхнего блока "приготовить кофе", но если эта задача выполняется впервые, то она разбивается на ряд микро-задач, объединенных связями, как показано на рис. 6. Вы видите, что сначала главная задача разбивается на три под-задачи, которые далее еще более детализируются.

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

Приготовление кофе

о

Если кофейник пуст, наполнить водой

Вскипятить воду

Смешивание

Включить нагреватель

Ждать, пока вода закипит

Взять чашку Насыпать кофе Залить воду

о

Если надо добавить сахар

Если надо добавить молоко

Рис.6

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

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

Итак, если Вы хотите, чтобы в Вашем рисунке одновременно сочетались детальность и компактность, то структурные диаграммы гораздо удобнее традиционных блок-схем.

Как структурировать программу?

Может быть сначала это кажется несколько необычным, но поверьте, потратить усилия стоят. Это вполне окупится.

Как только Вы освоите структурное программирование, то сразу увидите поразительную вещь, что в мире существуют всего лишь несколько видов программ! Вы увидите, что основные структуры из раза в раз повторяются и повторяются.

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

Например, если в конце текстовых записей в файле стоит маркер, говорящий о том, что записи закончились, то определенно в программе будет блок типа:

UNTIL

маркер Обработка данных

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

Начните с того, что перечислите главные задачи, выполняемые программой. После этого попробуйте рассортировать их по порядку очередности исполнения.

Следующий шаг - наметьте основные циклы повторений исполнения этих главных

задач.

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

Вам знакома игра SPACE INVADERS? На рис. 7 приведена структурная диаграмма этой игры и, надо сказать, в эту структуру уложились бы еще сотни разных "стрелялок".

Попробуем шаг за шагом подойти к этой структуре.

1. Запишем основные задачи.

Перемещение вашей базы.

Огонь.

Перемещение кораблей противника.

Построение игровой страницы (экрана).

Ведение счета очков.

Инициализация волны вражеского налета.

Три жизни.

Начать новую игру по окончании.

На этом этапе мы пока не задумывались над очередностью исполнения этих задач.

2. Наметим основные циклы.

Здесь очень важно правильно соблюсти порядок.

После хорошего размышления у нас может получиться нечто следующее:

Игра SPACE INVADERS состоит из ряда игр, каждая из которых состоит из трех попыток (три жизни), каждая из которых включает несколько страниц (экранов), каждый из которых может многократно изменяться.

3. Теперь будем рисовать структурную диаграмму.

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

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

Если бы мы сочли эту задачу достаточно важной для рассмотрения, то должны были бы встроить еще один уровень где то между уровнем организации экранов и уровнем изменения экранов. С другой стороны, если этот элемент игры не считать таким уж очень важным, поскольку он не оказывает существенного влияния на код остальной части игры, я бы добавил его в нижний уровень, сделав условным (IF).

Рис. 7

Продолжительность циклов: 1. Бесконечно или до отказа от продолжения игры.

2. Три раза.

3. До гибели.

4. До гибели или по окончании волны атаки.

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

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

Процедура INPUT

В завершение данного выпуска я предлагаю процедуру, которая в машинных кодах выполняет то, что делает команда БЕЙСИКа INPUT.

Если бы Вы знали, как много существует коммерческих программ, в которых при вводе данных по запросу от программы нельзя стереть допущенную ошибку или вставить пропущенную букву! Если Вы используете эту процедуру вместе с процедурой PRINT, напечатанной в прошлом выпуске, то сможете в машиннокодовых программах выполнять INPUT и PRINT, как если Вы работаете в Бейсике, только конечно гораздо быстрее.

Я привожу здесь же в учебных целях и структурную диаграмму этой процедуры (рис. 8). Посмотрите на нее и постарайтесь разыскать показанные на ней блоки в программном коде.

Для того, чтобы воспользоваться процедурой, Вам надо сначала напечатать сообщение (запрос) от программы. Это можно сделать процедурой прошлого выпуска PRINT. Затем надо в регистровой паре DE установить координаты экранной позиции, в которой выполняется INPUT (D - координата y в пикселях; E - координата x в знакоместах).

Предполагаемая длина вводимой информации задается в регистре A (от 1 до 32 символов).

Обратите внимание на то, что буфер INPUT сделан в том же формате, который поддерживается подпрограммой PRINT. Первый байт буфера содержит длину самого буфера.

INPUT

Обработка нажатой клавиши

Инициализация буфера

Очистка буфера

Определение нажатой клавиши (GETKEY)

Изображение буфера на экране

Определение какая клавиша нажата

О

CAPS LOCK

О

Символ

О

Курсор вправо

О

Курсор влево

О

DELETE

Рис.8

0000

00010

0000

00020

ORG 0М00Н

C000

00030

;Пример программы INPUTX

C000

00040

C000 110000

00050

LD DE,0

Координаты x,y

C003 2124C0

00060

LD HL,MESS1

C006 CD88C1

00070

CALL PRINT

Печать сообщения

C009 111000

00080

LD DE,10

Координаты x,y

C00C 3E10

00090

LD A,16

Длина поля

C00E CD42C0

00100

CALL INPUTX

C011

00110

C011

00120

Данные для ввода

C011

00130

дятся в буфере и

C011

00140

к использованию

C011

00150

C011 110010

00160

LD DE,1000H

C014 2134C0

00170

LD HL,MESS2

C017 CD88C1

00180

CALL PRINT

Печать сообщ.

C01A 110E10

00190

LD DE,100EH

C01D 214DC1

00200

LD HL,LENGH

C020 CD88C1

00210

CALL PRINT

Печать резуль.

C023 C9

00220

RET

C024

00230

C024

00240 MESS1

DB 15

C025 454E5445

00250

DM 'ENTER YOUR NAME

C029 5220594F

C02D

5552204E

C031

414D45

C034

0D

00260

MESS2

DB 13

C035

594F5552

00270

DM 'YOUR NAME IS'

C039

204E414D

C03D

45204953

C041

20

C042

00280

C042

00290

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

C042

00300

C042

00310

; Условия вызова процедуры

C042

00320

; INPUTX:

C042

00330

; A - длина

вводимых данных 1-32

C042

00340

; D - y координата (0 - верх)

C042

00350

; E - x координата

C042

00360

C042

00370

INPUTX

;Начало процедуры

C042

00380

;Инициализация буфера

C042

324DC1

00390

OK

LD (LENGTH),A

C045

6F

00400

LD L,A

C046

ED5375C1

00410

LD (OUTPOS),DE

C04A

AE

00420

XOR A

C04B

3E73C1

00430

LD (CURSOR),A

C04E

67

00440

LD H,A

C04F

114EC1

00450

LD DE,BUFFER

C052

19

00460

ADD HL,DE

C053

2270C1

00470

LD (BUFEND),HL

C056

3A4DC1

00480

LD A,(LENGTH)

C059

47

00490

LD B,A

C05A

214EC1

00500

LD HL,BUFFER

C05D

367F

00510

LD (HL),CURS

C05F

23

00530

INC HL

C060

3E2D

00530

LD A,ULINE

C062

77

00540

CLEAR

LD (HL),A

C063

23

00550

INC HL

C064

10FC

00560

DJNZ CLEAR

C066

213B5C

00570

LD HL,FLAGS

C069

CBAE

00580

RES 5,(HL)

C06B

00590

C06B

00600

;Обработка принятой клавиши

C06B

00610

;в ожидании

клавиши ENTER

C06B

00620

C06B

00630

ACCEPT

C06B

ED5BT5C1

00640

LD DE,(OUTPOS)

C06F

214DC1

00600

LD HL,LENGTH

C072

CD88C1

00660

CALL PRINT

C075

00670

C075

00680

;Прием очередной клавиши

C075

00690

C075

213B5C

00700

GETKEY

LD HL,FLAGS

C078

CB6E

00710

BIT 5,(HL)

C07A

28F9

00720

JR Z,GETKEY

C07C

3A065C

00730

LD A,(LASTK)

C07F

CBAF

00740

RES 5,(HL)

C061

FE06

00750

CP 6

C083

38F0

00760

JR C,GETKEY

C085

FE0D

00770

CP 0DH

C087

CA2FC1

00780

JP Z,ENTER

TO8A

FE08

00790

CP 8

C08C

283A

00800

JR Z,CRLEFT

C08E

FE09

00810

CP 9

C090

284F

00820

JR Z,CRRIGT

C092

FE0C

00830

CP 0CH

C094

2669

00840

JR Z,DELETE

C096

FE06

00850

CP 6

C098

CA25C1

00860

JP Z,CAPSLK

C09B

FE20

00870

CP 20H

C09D

38D6

00880

JR C,GETKEY

C09F

FE80

00890

CP 80H

C0A1

30D2

00900

JR NC,GETKEY

C0A3

00910

C0A3

00920

;Если принят печатный символ

C0A3

00930

C0A3

3274C1

00940

LD (LETTER),A

C0A6

3A73C1

00950

LD A,(CURSOR)

C0A9

47

00960

LD B,A

C0AA

3A4DC1

00970

LD A,(LENGTH)

C0AD

90

00980

SUB B

C0AE

8805

00990

JK Z,GETKEY

C0B0

4F

01000

LD C,A

C0B1

78

01010

LD A,B

C0B2

3C

01020

INC A

C0B3

3273C1

01030

LD (CURSOR),A

C0B6

01040

C0B6

ED5B70C1

01050

LD DE,(BUFEND)

C0BA

2A70C1

01060

LD HL,(BUFEND)

C0BD

2B

01070

DEC HL

C0BE

0600

01080

LD B,0

C0C0

EDB8

01090

LDDR

C0C2

3A74C1

01100

LD A,(LETTER)

C0C5

12

01110

LD (DE),H

C0C6

18A3

01120

JR ACCEPT

C0C8

01130

C0C8

01140

;Если курсор влево

C0C8

01150

C0C8

3A73C1

01160

CRLEFT LD A,(CURSOR)

C0CB

A7

01170

AND A

C0CC

28A7

01180

JR Z,GETKEY

C0CE

3D

01190

DEC A

C0CF

3273C1

01200

LD (CURSOR),A

C0D2

214EC1

01210

LD HL,BUFFER

C0D5

5F

01220

LD E,A

C0D6

1600

01230

LD D,0

C0D8

19

01240

ADD HL,DE

C0D9

7E

01250

LD A,(HL)

C0DA

367F

01260

LD (HL),CURS

C0DC

23

01270

INC HL

C0DD

77

01280

LD (HL),A

C0DE

C36BC0

01290

JP ACCEPT

C0E1

01300

C0E1

01310

;Если курсор вправо

C0E1

01320

C0E1

01330

CRRIGT

C0E1

3A4DC1

01340

LD A,(LENGTH)

C0E4

4F

01350

LD C,A

C0E5

3A73C1

01360

LD A,(CURSOR)

C0E8

B9

01370

CP C

C0E9

CA75C0

01360

JP Z,GETKEY

C0EC

3C

01390

INC A

C0ED

3273C1

01400

LD (CURSOR),A

C0F0

214EC1

01410

LD HL,BUFFER

C0F3

5F

01420

LD E,A

C0F4

1600

01430

LD D,0

C0F6

19

01440

ADD HL,DE

C0F7

7E

01450

LD A,(HL)

C0F6

367F

01460

LD (HL),CURS

C0FA

2B

01470

DEC HL

C0FB

77

01480

LD (HL),A

C0FC

C36BC0

01490

JP ACCEPT

C0FF

01500

;Временный стек

;Буфер полон ;Смещение курсора

C0FF

01510

;Если DELETE

C0FF

01520

C0FF

01530

DELETE

C0FF

3A73C1

01540

LD A,(CURSOR)

C102

A7

01550

AND A

C103

CA75C0

01560

JP Z,GETKEY

C106

5F

01570

LD E,A

C107

3A4DC1

01560

LD A,(LENGTH)

C10A

93

01590

SUB E

C10B

3C

01600

INC A

C10C

4F

01610

LD C,A

C10D

7B

01620

LD A,E

C10E

3D

01630

DEC A

;Курсор влево

C10F

3273C1

01640

LD (CURSOR),A

C112

01650

C112

214EC1

01660

LD HL,BUFFER

C115

1600

01670

LD D,0

C117

19

01680

ADD HL,DE

C116

54

01690

LD D,H

C119

5D

01700

LD E,L

C11A

1B

01710

DEC DE

C11B

0600

01720

LD B,0

C11D

EDB0

01730

LDIR

C11F

3E2D

01740

LD A,ULINE

C121

12

01750

LD (DE),A

C122

C36BC0

01760

JP ACCEPT

C125

01770

C125

01780

;Если CAPS LOCK

C125

01790

C125

216A5C

01800

CAPSLK

LD HL,5C6AH

C128

3E08

01810

LD A,8H

C12A

AE

01820

XOR (HL)

C12B

77

01830

LD (HL),A

C12C

C36BC0

01840

JP ACCEPT

C12F

01850

C12F

01860

;Если ENTER

C12F

01670

C12F

01860

ENTER

C12F

114EC1

01890

LD DE,BUFFER

C132

214EC1

01900

LD HL,BUFFER

C135

3A4DC1

01910

LD A,(LENGTH)

C138

47

01920

LD B,A

C139

7E

01930

CLEAN

LD A,(HL)

C13A

23

01940

INC HL

C13B

FE7F

01950

CP CURS

C13D

2808

01960

JR Z,IGNORE

C13F

FE2D

01970

CP ULINE

C141

2002

01980

JR NZ,PACK

C143

3E20

01990

LD A,SPACE

C145

12

02000

PACK

LD (DE),A

C146

13

02010

INC DE

C147

10F0

02020

IGNORE

DJNZ CLEAN

C149

3E20

02030

LD A,SPACE

C14B

12

02040

LD (DE),A

C14C

C9

02050

RET

C14D

02060

C14D

02070

C14D

02060

;Переменные буфера

C14D

0E090

C14D

00

02100

LENGTH

DB 0

C14E

02110

BUFFER

DS 34 ;

Резервирует 34 б

C170

02120

C170

0000

02130

BUFEND

DW 0

002D

02140

ULINE

EQU '-'

007F

02150

CURS

EQU 127

0020

02160

SPACE

EQU ' '

C172

02170

C172

02180

;Настройка

переменных печати

C172

02190

5C3B

02200

FLAGS

EQU 5C3BH

5C6A

02210

FLAGS2

EQU 5C6AH

5C08

02220

LASTK

EQU 23560

с172

00

02230

COUNT

DB 0

C173

00

02240

CURSOR

DB 0

C174

00

02250

LETTER

DB 0

C175

0000

02260

OUTPOS

DW 0

C177

02270




СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Музыкалка - Обзор музыкальных демонстраций: Absent, Shock.
Рабочий стол - O возможностях нового коммандера "Win Commander 0.02".
Урок английского - Александр КАМНЕВ, Борис ФАЙФЕЛЬ: Урок английского.
От автора - Новости из FIDO...
Железо - схема аппаратных часов для ZX spectrum.

В этот день...   3 декабря