Урок 4. Программа с меню: семь рисунков

В картинках

Схема готова — теперь программа. На дискете PLOTTER4.dsk лежит программа PLOTTER на Applesoft BASIC: меню из семи рисунков, каждый в несколько цветов. Компьютер ведёт перо по точкам, называет цвет, ждёт, пока револьвер довернёт, — всё через те же три адреса, что ты проверял в уроке 3. В этом уроке ничего не собираем: ставим дискету, запускаем и разбираем, как программа устроена.


4.1. Идея: вся программа — три адреса

Программа ничего не знает о триггерах и моторах. Она пользуется только тем, что плата обещает на шине:

В программеЧто это
B = 49296$C090: POKE B,D — шаги, направления, перо
PEEK (49297)$C091: концевики и BUSY
R = 49298$C092: POKE R,C — цвет, револьвер доворачивает

Так устроены все устройства компьютера: программа — с одной стороны адресов, железо — с другой. Начало программы — адреса в переменных и меню:

10 REM COLOR PLOTTER20 B = 49296: R = 4929830 P = 0: X = 0: Y = 0: C = 040 C2 = 0: GOSUB 1300: GOSUB 90050 HOME : PRINT "COLOR PLOTTER": PRINT60 PRINT "1 HOUSE       5 SPIRAL"62 PRINT "2 RAINBOW     6 HELLO"64 PRINT "3 FLOWER      7 SPIROGRAPH"66 PRINT "4 STAR        0 EXIT": PRINT70 INPUT "PICTURE: ";K72 IF K = 0 THEN C2 = 0: GOSUB 1300: END80 IF K < 1 OR K > 7 THEN 5090 PRINT "PUT A NEW SHEET, PRESS RETURN";92 GET A$: PRINT100 ON K GOSUB 2000,2100,2200,2300,2400,2500,2600110 P = 0: GOSUB 900: GOTO 50

4.2. Идея: «домой» по концевикам (строки 900–940)

Где сейчас каретки, компьютер не знает. Поэтому в начале и после каждого рисунка — «домой»: поднять перо и шагать к мотору, пока не загорится концевик.

900 REM HOME: TO THE LIMIT SWITCHES910 P = 0: POKE B,0920 L = PEEK (49297)922 IF L - INT (L / 2) * 2 THEN 930924 POKE B,1: POKE B,0: GOTO 920930 L = INT ( PEEK (49297) / 4)932 IF L - INT (L / 2) * 2 THEN 940934 POKE B,4: POKE B,0: GOTO 930940 X = 0: Y = 0: RETURN
  • 920–924: пока XL0 = 0 — шаг X к мотору (POKE B,1: POKE B,0 — фронт на XSTEP, XDIR = 0);
  • 930–934: то же по Y до YL0.

Бит из числа BASIC достаёт делением: бит 0 — L - INT(L/2)*2 (остаток от деления на 2), бит 2 — то же от INT(PEEK(49297)/4). IF … THEN 930 срабатывает, если бит не 0. Дома — X = 0, Y = 0: дальше программа сама считает, где перо.

4.3. Идея: прямая из шагов (строки 1000–1090)

Мотор шагает только по X или по Y, ровно на одну точку. Прямую от (X, Y) до (TX, TY) собирают из шагов: всего шагов N — по большей из разниц, AX = |TX - X| или AY = |TY - Y|. На каждом из N шагов к счётчикам EX и EY прибавляется AX и AY; набралось N — шаг по этой оси, счётчик минус N. По длинной оси шаг бывает на каждом проходе, по короткой — через раз, через два… равномерно. Это алгоритм Брезенхема, по нему рисуют и плоттеры, и экраны.

1000 REM LINE TO TX,TY, PEN P1010 DX = TX - X: DY = TY - Y1012 AX = ABS (DX): AY = ABS (DY)1020 D = P + (DX > 0) * 2 + (DY > 0) * 81022 POKE B,D1030 N = AX: IF AY > N THEN N = AY1040 IF N = 0 THEN 10901050 EX = INT (N / 2): EY = EX1060 FOR I = 1 TO N: S = 01062 EX = EX + AX: EY = EY + AY1070 IF EX > = N THEN EX = EX - N: S = 11080 IF EY > = N THEN EY = EY - N: S = S + 41085 POKE B,D + S: POKE B,D: NEXT I1090 X = TX: Y = TY: RETURN1100 REM MOVE TO TX,TY, PEN UP1110 P = 0: GOTO 1000

Одна строка 1085 — один шаг: POKE B,D + S (фронты на выбранных осях) и POKE B,D (обратно, чтобы следующий шаг снова был фронтом). В D — направления и перо: P + (DX > 0) * 2 + (DY > 0) * 8.

4.4. Идея: цвет и ожидание (строки 1300–1330)

1300 REM PEN C2: WAIT WHILE BUSY1310 P = 0: POKE B,01312 POKE R,C2: C = C21320 BU = INT ( PEEK (49297) / 16)1322 IF BU - INT (BU / 2) * 2 THEN 13201330 RETURN
  • 1310–1312: поднять перо и назвать цвет: POKE R,C2;
  • 1320–1322: читать $C091, пока бит 4 (BUSY) равен 1: делим на 16 и берём остаток от деления на 2.

Пока револьвер крутится, программа стоит в этой строке. Перо в рабочей точке опустится только после доворота — так сделано в железе урока 2, а программа просто ждёт.

4.5. Идея: рисунок — таблица в DATA (строки 1500–1570, 3000…)

Домик, цветок, надпись — это набор отрезков. Чтобы не писать каждый отрезок кодом, рисунки лежат в строках DATA на маленьком «языке плоттера»:

СловоЧто делает
P,Kначало рисунка номер K
C,nвзять перо n (0–3)
M,x,yперейти, перо поднято
L,x,yлиния, перо опущено
Eконец рисунка

Домик в DATA:

3000 REM PICTURES: P,K C,PEN M,X,Y L,X,Y E3010 DATA P,1,C,0,M,80,140,L,200,1403020 DATA L,200,250,L,80,250,L,80,1403030 DATA C,1,M,70,140,L,140,803040 DATA L,210,140,L,70,1403050 DATA C,3,M,100,165,L,140,1653060 DATA L,140,205,L,100,205,L,100,1653070 DATA M,120,165,L,120,205,M,100,1853080 DATA L,140,1853090 DATA C,0,M,160,250,L,160,1953100 DATA L,185,195,L,185,2503110 DATA C,2,M,20,250,L,380,2503120 DATA M,30,250,L,36,236,L,42,2503130 DATA M,230,250,L,236,236,L,242,2503140 DATA M,300,250,L,306,236,L,312,2503150 DATA E

Подпрограмма 1500 ищет P,K и выполняет слова по одному:

1500 REM PICTURE K FROM DATA1510 RESTORE1520 READ A$: IF A$ < > "P" THEN 15201530 READ N: IF N < > K THEN 15201540 READ A$: IF A$ = "E" THEN P = 0: RETURN1550 IF A$ < > "C" THEN 15601552 READ C2: GOSUB 1300: GOTO 15401560 READ TX,TY1562 IF A$ = "M" THEN GOSUB 1100: GOTO 15401570 P = 16: GOSUB 1000: GOTO 1540

Радуга, спираль и спирограф считаются формулой (синус и косинус), а солнце у домика и серединка цветка — окружность из коротких отрезков:

1400 REM CIRCLE CX,CY, RADIUS RR1410 TX = CX + RR: TY = CY: GOSUB 11001412 P = 161420 FOR J = 1 TO NS1422 A = 6.28318 * J / NS1424 TX = INT (CX + RR * COS (A) + .5)1426 TY = INT (CY + RR * SIN (A) + .5)1428 GOSUB 1000: NEXT J1430 P = 0: RETURN

Спирограф — формула гипотрохоиды: колёсико радиусом 45 катится в кольце 105, перо в 60 от его центра:

2600 REM 7 SPIROGRAPH2610 TX = 320: TY = 150: GOSUB 11002620 FOR Q = 0 TO 22622 C2 = Q + 1: GOSUB 1300: P = 162630 FOR J = 1 TO 602632 T = 6.28318 * (Q + J / 60)2634 TX = 200 + 60 * COS (T)2636 TX = INT (TX + 60 * COS (4 * T / 3) + .5)2638 TY = 150 + 60 * SIN (T)2640 TY = INT (TY - 60 * SIN (4 * T / 3) + .5)2642 GOSUB 1000: NEXT J2644 P = 0: NEXT Q: RETURN

4.6. Как запустить

  1. Режим Turbo, скорость — MAX (на ней рисунок идёт за минуту-две).
  2. REMOTE включён; каретки можно оставить где угодно — программа сама отправит их домой.
  3. У дисковода (Disk II) на панели ⬆ — выбери файл набора PLOTTER4.dsk.
  4. В BASIC: RUN PLOTTER.
  5. Меню: цифра рисунка и Return. «PUT A NEW SHEET, PRESS RETURN» — очисти лист (на панели бумаги) и нажми Return.
  6. Нарисовал — каретки уходят домой, снова меню. 0 — выход: револьвер вернёт чёрное перо.

4.7. Проверка

№РисунокЧто на листе
1HOUSEдом чёрным, крыша и солнце с лучами красным, окно
синим, дверь чёрным, трава зелёным
2RAINBOWчетыре дуги: красная, зелёная, синяя, чёрная
3FLOWERшесть красных лепестков, зелёный стебель с листьями,
чёрная серединка
4STARпятиконечная звезда, каждая сторона своим цветом
5SPIRALспираль в четыре витка: чёрный, красный, зелёный,
синий
6HELLOбуквы: H красная, E зелёная, L L синие, O чёрная
7SPIROGRAPHузор из трёх кругов: красный, зелёный, синий

Пока револьвер крутится, горит BUSY и ни одно перо не касается бумаги — на листе нет дуг от поворота.


Программа PLOTTER целиком

Она уже на дискете. Можно набрать и самому — в BASIC, строку за строкой (пробелы можно не ставить), потом SAVE PLOTTER.

10 REM COLOR PLOTTER20 B = 49296: R = 4929830 P = 0: X = 0: Y = 0: C = 040 C2 = 0: GOSUB 1300: GOSUB 90050 HOME : PRINT "COLOR PLOTTER": PRINT60 PRINT "1 HOUSE       5 SPIRAL"62 PRINT "2 RAINBOW     6 HELLO"64 PRINT "3 FLOWER      7 SPIROGRAPH"66 PRINT "4 STAR        0 EXIT": PRINT70 INPUT "PICTURE: ";K72 IF K = 0 THEN C2 = 0: GOSUB 1300: END80 IF K < 1 OR K > 7 THEN 5090 PRINT "PUT A NEW SHEET, PRESS RETURN";92 GET A$: PRINT100 ON K GOSUB 2000,2100,2200,2300,2400,2500,2600110 P = 0: GOSUB 900: GOTO 50900 REM HOME: TO THE LIMIT SWITCHES910 P = 0: POKE B,0920 L = PEEK (49297)922 IF L - INT (L / 2) * 2 THEN 930924 POKE B,1: POKE B,0: GOTO 920930 L = INT ( PEEK (49297) / 4)932 IF L - INT (L / 2) * 2 THEN 940934 POKE B,4: POKE B,0: GOTO 930940 X = 0: Y = 0: RETURN1000 REM LINE TO TX,TY, PEN P1010 DX = TX - X: DY = TY - Y1012 AX = ABS (DX): AY = ABS (DY)1020 D = P + (DX > 0) * 2 + (DY > 0) * 81022 POKE B,D1030 N = AX: IF AY > N THEN N = AY1040 IF N = 0 THEN 10901050 EX = INT (N / 2): EY = EX1060 FOR I = 1 TO N: S = 01062 EX = EX + AX: EY = EY + AY1070 IF EX > = N THEN EX = EX - N: S = 11080 IF EY > = N THEN EY = EY - N: S = S + 41085 POKE B,D + S: POKE B,D: NEXT I1090 X = TX: Y = TY: RETURN1100 REM MOVE TO TX,TY, PEN UP1110 P = 0: GOTO 10001300 REM PEN C2: WAIT WHILE BUSY1310 P = 0: POKE B,01312 POKE R,C2: C = C21320 BU = INT ( PEEK (49297) / 16)1322 IF BU - INT (BU / 2) * 2 THEN 13201330 RETURN1400 REM CIRCLE CX,CY, RADIUS RR1410 TX = CX + RR: TY = CY: GOSUB 11001412 P = 161420 FOR J = 1 TO NS1422 A = 6.28318 * J / NS1424 TX = INT (CX + RR * COS (A) + .5)1426 TY = INT (CY + RR * SIN (A) + .5)1428 GOSUB 1000: NEXT J1430 P = 0: RETURN1500 REM PICTURE K FROM DATA1510 RESTORE1520 READ A$: IF A$ < > "P" THEN 15201530 READ N: IF N < > K THEN 15201540 READ A$: IF A$ = "E" THEN P = 0: RETURN1550 IF A$ < > "C" THEN 15601552 READ C2: GOSUB 1300: GOTO 15401560 READ TX,TY1562 IF A$ = "M" THEN GOSUB 1100: GOTO 15401570 P = 16: GOSUB 1000: GOTO 15402000 REM 1 HOUSE AND SUN2010 GOSUB 15002012 C2 = 1: GOSUB 13002014 CX = 320: CY = 60: RR = 28: NS = 162016 GOSUB 14002020 FOR Q = 0 TO 7: A = Q * .7853982022 TX = INT (CX + 36 * COS (A) + .5)2024 TY = INT (CY + 36 * SIN (A) + .5)2026 GOSUB 11002030 TX = INT (CX + 50 * COS (A) + .5)2032 TY = INT (CY + 50 * SIN (A) + .5)2034 P = 16: GOSUB 1000: P = 02036 NEXT Q: RETURN2100 REM 2 RAINBOW2110 FOR Q = 0 TO 32112 C2 = Q + 1 - 4 * (Q = 3): GOSUB 13002114 RR = 150 - Q * 182116 TX = 200 - RR: TY = 250: GOSUB 11002118 P = 162120 FOR J = 1 TO 242122 A = 3.14159 + 3.14159 * J / 242124 TX = INT (200 + RR * COS (A) + .5)2126 TY = INT (250 + RR * SIN (A) + .5)2128 GOSUB 1000: NEXT J2130 P = 0: NEXT Q: RETURN2200 REM 3 FLOWER2210 C2 = 1: GOSUB 13002212 TX = 200: TY = 110: GOSUB 11002214 P = 162220 FOR J = 1 TO 722222 A = 6.28318 * J / 722224 RR = 70 * ABS ( SIN (3 * A))2226 TX = INT (200 + RR * COS (A) + .5)2228 TY = INT (110 + RR * SIN (A) + .5)2230 GOSUB 1000: NEXT J: P = 02232 GOSUB 1500: C2 = 0: GOSUB 13002234 CX = 200: CY = 110: RR = 8: NS = 82236 GOSUB 1400: RETURN2300 REM 4 STAR2310 FOR Q = 0 TO 42312 A = - 1.5708 + Q * 2.513272314 TX = INT (200 + 120 * COS (A) + .5)2316 TY = INT (150 + 120 * SIN (A) + .5)2320 IF Q = 0 THEN GOSUB 1100: GOTO 23402330 C2 = Q - 4 * (Q = 4): GOSUB 13002332 P = 16: GOSUB 1000: P = 02340 NEXT Q2342 C2 = 0: GOSUB 13002344 TX = 200: TY = 302346 P = 16: GOSUB 1000: P = 0: RETURN2400 REM 5 SPIRAL2410 TX = 210: TY = 150: GOSUB 11002420 FOR Q = 0 TO 32422 C2 = Q: GOSUB 1300: P = 162430 FOR J = 1 TO 362432 A = 6.28318 * (Q + J / 36)2434 RR = 10 + 30 * (Q + J / 36)2436 TX = INT (200 + RR * COS (A) + .5)2438 TY = INT (150 + RR * SIN (A) + .5)2440 GOSUB 1000: NEXT J2442 P = 0: NEXT Q: RETURN2500 REM 6 HELLO2510 GOSUB 1500: RETURN2600 REM 7 SPIROGRAPH2610 TX = 320: TY = 150: GOSUB 11002620 FOR Q = 0 TO 22622 C2 = Q + 1: GOSUB 1300: P = 162630 FOR J = 1 TO 602632 T = 6.28318 * (Q + J / 60)2634 TX = 200 + 60 * COS (T)2636 TX = INT (TX + 60 * COS (4 * T / 3) + .5)2638 TY = 150 + 60 * SIN (T)2640 TY = INT (TY - 60 * SIN (4 * T / 3) + .5)2642 GOSUB 1000: NEXT J2644 P = 0: NEXT Q: RETURN3000 REM PICTURES: P,K C,PEN M,X,Y L,X,Y E3010 DATA P,1,C,0,M,80,140,L,200,1403020 DATA L,200,250,L,80,250,L,80,1403030 DATA C,1,M,70,140,L,140,803040 DATA L,210,140,L,70,1403050 DATA C,3,M,100,165,L,140,1653060 DATA L,140,205,L,100,205,L,100,1653070 DATA M,120,165,L,120,205,M,100,1853080 DATA L,140,1853090 DATA C,0,M,160,250,L,160,1953100 DATA L,185,195,L,185,2503110 DATA C,2,M,20,250,L,380,2503120 DATA M,30,250,L,36,236,L,42,2503130 DATA M,230,250,L,236,236,L,242,2503140 DATA M,300,250,L,306,236,L,312,2503150 DATA E3160 DATA P,3,C,2,M,200,180,L,200,2903170 DATA M,200,250,L,170,225,L,200,2353180 DATA M,200,230,L,230,205,L,200,2153190 DATA E3200 DATA P,6,C,1,M,30,100,L,30,2003210 DATA M,30,150,L,90,150,M,90,1003220 DATA L,90,2003230 DATA C,2,M,170,100,L,110,1003240 DATA L,110,200,L,170,200,M,110,1503250 DATA L,155,1503260 DATA C,3,M,190,100,L,190,2003270 DATA L,250,200,M,270,100,L,270,2003280 DATA L,330,2003290 DATA C,0,M,350,100,L,390,1003300 DATA L,390,200,L,350,200,L,350,1003310 DATA E

Не получается? Что-то не работает →