Idealnym rozwiązaniem byłoby stworzenie algorytmu zamalowywania dowolnego obszaru zamkniętego. Poniżej przedstawiam jeden z nich. Pomysł jest ogólnie znany (Sinclair User) i mimo to, że procedura działa wolno, jest ona bardzo krótka i mało skomplikowana.
Każdemu punktowi ekranu przyporządkujmy jego współrzędne w układzie tak, jak na ekranie. Wypełnianie rozpocznie się od ostatniego postawionego punktu, jego współrzędne odczytane ze zmiennej systemowej COORDS (23 677 i 23 678). Główna pętla programu to linie 120-180. Pod uwagę bierzemy punkt o współrzędnych (x,y) — zapisujemy x jako xp i y jako yp. Teraz patrzymy na punkt powyżej rozpatrywanego i — gdy jest zgaszony — zapalamy go zapamiętując jego współrzędne w tablicy i zwiększamy wskaźnik p określający, ile mamy zapamiętanych punktów. Jeżeli punkt jest zapalony, patrzymy na punkt poniżej (xp, yp) i ewentualnie zapalamy go (podprogram 200). Postępowanie powtarzamy dla punktu z lewej i z prawej strony. Następnie sprawdzamy, czy p jest równe zero. Jeśli tak, to zapełnianie zostało zakończone (żaden z podstawionych punktów nie ma zgaszonego sąsiada). Jeśli nie, odczytujemy z tablicy współrzędne ostatnio zapalonego punktu (w tym przypadku jest to punkt położony po prawej punktu (xp, yp)) i jego bierzemy pod uwagę przebiegając linie 150-180. Jeśli natrafimy na punkt, którego wszyscy sąsiedzi są zapaleni, cofamy się w tablicy zmniejszając p i szukając punktu, którego któryś z sąsiadów nie jest zapalony.
W ten sposób zamalujemy każdy obszar zamknięty przez postawienie wewnątrz niego punktu i skok do linii 100. Jedynym ograniczeniem jest rozmiar tablicy. Jeżeli ilość zapamiętanych i nie usuniętych punktów przekroczy 2000, program zatrzyma się komunikatem Subscript wrong. Na ekranie mamy 256x176=45 056 punktów i zapamiętanie wszystkich byłoby niemożliwe, i chociaż program „cofając się” (zmniejszając p) oszczędnie gospodaruje pamięcią, to jednak nie zamaluje całego ekranu nawet startując ze środka.
Dla przykładu: obrazkiem do zamalowania będzie misio Narysujemy go, wpisując:
15 LET su=0
10 FOR a=l TO 10
20 READ x,y,r
30 CIRCLE x,y,r: LET su = su + x + y+r
40 NEXTa: IF su<>2350 THEN PRINT „Zle dane”: STOP
50 DATA 128, 118, 30. 167, 140. 15, 89, 140, 15, 115, 125, 5, 141, 125, 5, 128, 58, 30, 95, 77, 8, 161
77, 8, 161, 39, 8, 95, 39, 8
55 LETsu = 0
60 FOR a-l TO 10 65
READ x,y
70 PLOT OVER I: x,y: LET su=su+x+y
80 NEXT a: IF su<>2056 THEN PRINT „Zle dane”: STOP
90 DATA 103, 134, 154, 134, 128, 88, 102, 72, 155, 72, 156, 72, 102, 43, 102. 43. 154, 43, .155. 43
95 PLOT 128, 86
przed programem zamalowywania (listing 1). Misio składa się z kółek (linie 5—50) z przejściami między nimi (linie 55—90). Start zapełniania podaje linia 95.
Widać, że zamalowywanie trwa bardzo długo. Istotne przyspieszenie uzyskamy pisząc ten sam program w asemblerze (listing 2). Schemat działania jest taki sam, jak poprzednio. Zamiast tablicy użyjemy stosu systemowego. Zamiast znacznika długości (poprzednio p) na początku położymy na stos liczbę 255. Odczytanie jej ze stosu będzie równoznaczne z zakończeniem malowania (poprzednio p=0).
Procedura odwołuje się do ROM-u Spectrum wykorzystując zawarte tam procedury PLOT (8933) i POINT (8910), oraz FP-TO-A (11 733). Współrzędne punktów przechowywane są odpowiednio: x w C, y w B, xp w L i yp w H. Procedura PLOT zapala punkt o współrzędnych zawartych w C i B; procedura POINT sprawdza obecność punktu we wsp. C i B sygnalizuje to bitem w akumulatorze; procedura FP-TO-A zamienia ten bit na bajt (I — zapalony, 0 — zgaszony).
Ponieważ skok do podprocedury (CALL) także wykorzystuje stos, musiałem adres powrotu z procedury przechowywać w parze DE.
Jeżeli obszar nie jest zamknięty (zapełniamy cały ekran lub obszar przylega do brzegu ekranu), granice obszaru wyznaczają brzegowe linie ekranu (tak jakby było PLOT 0,0: DRAW 255,0: DRAWO, 175: DRAW —255,0: DRAW 0, — 175). Procedurę maszynową umieszczamy w pamięci program z listingu 3
Teraz zamalowanie misia to igraszka, lecz pamiętaj o postawieniu punktu wewnątrz niego.
Michał Szuniewicz