BMP2HIRES
Data
W poniższym tekście podejmiemy wyzwanie konwersji i wyświetlenia pliku w formacie BMP o wymiarach 320x200 w 2 kolorach na C64 – koderski level easy. Format pojawił się pierwotnie na systemach OS/2 i przeżył ten system prawdopodobnie ze względu na swoją prostotę i brak obciążeń patentowych.
No to otwieramy dowolny program graficzny na współczesnej maszynie. Ładujemy tam jakieś zdjęcie z wakacji z ulubionym psem albo dziewczyną i zmieniamy wymiary zdjęcia do 320x200 oraz jego głębię do 1 bit. Oczywiście warto zastosować konwersję przez rozproszenie pikseli. Teraz zapisujemy naszą fotkę w formacie BMP i ów plik ładujemy na C64. Co da się zauważyć od razu, to fakt, że nasz plik ładuje się pod adres $4D42. Dzieje się tak dlatego, że pierwsze dwa bajty pliku BMP utworzonego na systemie innym niż OS/2 w ASCII oznaczają „BM”. To fantastyczny zbieg okoliczności, gdyż pierwsze dwa bajty pliku PRG na C64 interpretowane są jako adres ładowania, a $4D42 to niemal początek drugiego banku pamięci VIC-II, który standardowo nie jest używany. Pasuje nam to idealnie, bo wiemy już, gdzie nasza procedura konwertująca ma szukać pliku w pamięci. Jednak to nie koniec uproszczeń. Kolejne 2 bajty pliku oznaczają jego wymiary razem z nagłówkiem i w naszym przypadku będzie to $1F7E. Dokładnie są to 4 kolejne bajty, ale pliki o głębi kolorów do 256 okupują i tak tylko pierwsze dwa. Wszystkie te bajty zapisane są w pliku w kolejności: mniej znaczący bajt najpierw, co też jest dla nas, programistów asemblera 65xx uproszczeniem. Tak więc znajdując od $4D42 kolejno #$7E i #$1F wiemy, że mamy do czynienia z plikiem BMP o pożądanych przez nas wymiarach i głębi.
LDA #$7E
CMP $4D42
BNE error
LDA #$1F
CMP $4D43
BNE error
...
Oczywiście możemy sobie utworzyć plik o rozdzielczości i liczbie kolorów, które sumarycznie dadzą z nagłówkiem taki sam rozmiar pliku, ale hej, nie będziemy się oszukiwać. Wiedząc, że bitmapa 320x200x1 zajmuje $1F40 bajtów oraz mając świadomość, że podczas ładowania pliku na C64 został on skrócony o adres ładowania (2 bajty), możemy sobie wyliczyć dokładny koniec pliku: $4D42+$1F7E-$2=$6CBE. Ostatni bajt naszej bitmapy do odczytu znajduje się pod adresem $6CBD, ale na tym uproszczenia się kończą, ponieważ ostatni bajt nie jest wcale ostatnim bajtem macierzy pikseli w grafice. Ze względu na historyczną kompatybilność z menadżerem prezentacji systemu OS/2 od IBM nasza macierz pikseli w pamięci zapisana jest do góry nogami. Po prostu ktoś w IBM wymyślił sobie, że fajniej będzie się kreśliło wykresy, gdy adres będzie zwiększał się wraz ze współrzędnymi układu kartezjańskiego. Ech ci matematycy... Tak więc grafika z pliku jest wczytywana od dołu do góry do bufora ramki na PC. Każdy, kto dysponował przeglądarką SEA pod MS-DOS oraz odpowiednio dużym plikiem BMP i odpowiednio wolnym komputerem, widział ten proces na własne oczy. Możemy więc rozpocząć wczytywanie do banku wideo albo od początku pliku i dostaniemy obrazek odwrócony do góry nogami, albo od końca pliku i dostaniemy obrazek odbity lustrzanie. Wszystko to jednak jedynie przy organizacji liniowej bufora ramki. Niestety, VIC-II w naszym C64 interpretuje w trybie bitmapowym grafikę tak, jak tekst, czyli 1000 bloków po 8 bajtów każdy następujących kolejno po sobie liniowo, a nie 8000 bajtów następujących kolejno po sobie liniowo. Stąd drugie podejście będzie w tym wypadku łatwiejsze do zaprogramowania. Czyli czytamy plik od tyłu, jak leci bajt po bajcie i zapisujemy od góry, lecz z prawej do lewej w organizacji kafelkowej. Dzięki temu żadna operacja na bitach w kopiowanych bajtach nie jest konieczna:
LDA #$3B ;tryb graficzny HIRES
STA $D011
LDA #$18
STA $D018
LDY #8 ;jeden kafelek ma 8 bajtów
LDX #$28 ;jedna linia ma 40 bajtów
source LDA $6CBD ;odczyt zaczynamy od końca pliku BMP
target STA $2138 ;zapis zaczynamy od końca 1 linii HIRES
LDA source+1;zmniejsz adres BMP o 1 bajt
BNE no1
DEC source+2
no1 DEC source+1
LDA target+1 ;zmniejsz adres HIRES o 8 bajtów
SEC
SBC #8
BCS no2
DEC target+2
no2 STA target+1
DEX
BNE source
LDA target+1 ;następna linia – zwiększ adres HIRES
CLC
ADC #$41
BCC no3
INC target+2
no3 INC target+2
STA target+1
DEY
BNE source-2
LDA target+1 ;następne 8 linii – zwiększ adres HIRES
CLC
ADC #$38
BCC no4
INC target+2
no4 INC target+2
STA target+1
LDA target+2 ;czy koniec bufora ramki?
CMP #$40
BNE source-4
RTS
Całość zajmuje niewiele ponad 80 bajtów i wykonuje się w ok. 1 sekundę. Czego chcieć zatem więcej? Programu, który zrobi to za nas. Ale o tym już niebawem.
Data/Tropyx