Commodore 64: renowacja i grafika 3D w BASIC-u, C i asemblerze

Przetłumaczone automatycznie z angielskiego. Przeczytaj oryginał

Odnowiony Commodore 64 podłączony do laptopa przez grabber USB

Zastrzeżenie: opisane tu projekty są wynikiem mojej własnej, samodzielnej pracy, wykonanej bez użycia AI. Sam artykuł powstał z pomocą AI.

W czerwcu 2018 roku dostałem Commodore 64 w oryginalnym pudełku. Katalog c64 w moim repozytorium retro-lab jest tego efektem: fotograficzny dziennik renowacji i trzy implementacje tego samego celu – obracającego się modelu 3D wireframe – w trzech językach, w których realnie da się programować tę maszynę.

Renowacja #

Komputer był kompletny, ale brudny i pożółkły. Rozebrałem go, wyczyściłem obudowę i każdy klawisz osobno, po czym złożyłem z powrotem.

Commodore 64 w oryginalnym pudełku przed renowacją

Tylne porty obudowy C64 podczas czyszczenia

Klawiatura z wyczyszczonymi klawiszami założonymi z powrotem na ramę

W moim domu nie ma już żadnego monitora z wejściem RF. Najwygodniejszym rozwiązaniem okazało się wyjście S-Video podłączone do taniego grabbera USB, z VirtualDubem wyświetlającym obraz na laptopie:

Odnowiony Commodore 64 podłączony do laptopa przez grabber USB

Na co dzień pracowałem na emulatorze VICE, a na prawdziwy sprzęt przechodziłem tylko po to, żeby sprawdzić wynik.

Jeden renderer, trzy języki #

Punktem odniesienia był generic, mój silnik 3D w JavaScripcie z 2013 roku. Jego matematyka (macierze obrotu, kamera, dzielenie perspektywiczne) jest na tyle prosta, że da się ją przenieść niemal na wszystko. Pytanie brzmiało, jak daleko da się z nią zajść na 6510 taktowanym 1 MHz z 64 KB RAM-u.

BASIC 2.0 #

Pierwsza wersja to zwykły Commodore BASIC (main.bas): macierze obrotu w tablicach DIM i rzut perspektywiczny każdego wierzchołka. Na razie nic nie rysuje. Zamiast tego wypisuje rzutowaną współrzędną i liczbę tyknięć zegara, jaką zajęły obliczenia, oraz FRE(1)+65536, żeby zobaczyć, ile pamięci zostało. O to właśnie chodziło: zmierzyć, jak kosztowna jest sama matematyka, zanim napiszę jakikolwiek kod graficzny.

Wpisywanie programu w BASIC-u w VICE

Rzutowane współrzędne i czas obliczeń dla każdego wierzchołka wypisane przez wersję w BASIC-u

Osobny program, bitmap_mode.bas, włącza tryb bitmapowy dwoma POKE-ami do VIC-II i wypełnia ekran kolorem, w nieskończoność przechodząc przez kolejne kolory:

basic
5 base=2*4096
9 poke 53272,peek(53272)or8
10 poke 53265,peek(53265)or32
15 co = 3
20 for i = base to base+7999: poke i,0: next
30 for i = 1024 to 2023: poke i, co: next
35 co = co + 1
40 goto 20

Najprościej uruchomić którykolwiek z tych listingów, wklejając go do VICE po NEW i wpisując RUN.

C z cc65 #

cc65 to kompilator C, asembler i linker dla rodziny 6502. Jedno polecenie tworzy program z nagłówkiem BASIC-a, więc startuje on przez zwykły autostart w VICE:

Bash
cl65 -r -O main.c -t c64

-r włącza zmienne register (cc65 pozwala na maksymalnie 6 na funkcję, trzymanych na stronie zerowej). Model to sześcian zdefiniowany jako wierzchołki i ściany, a renderer przerysowuje obraz tylko wtedy, gdy naciśnięcie klawisza zmienia położenie kamery.

Sześcian wireframe wyrenderowany przez wersję cc65 w VICE

Zastąpienie TGI

cc65 jest dostarczany z TGI, przenośną biblioteką graficzną. Jest wygodna, ale duża i wolna, więc zastąpiłem ją bezpośrednim dostępem do rejestrów VIC-II i CIA. Poniższy kod przełącza w tryb bitmapowy i wybiera bank VIC-II:

C
void video_init() {
  // DATA DIRECTION
  TMP1 = *(byte*) 0xDD02;
  TMP1 = TMP1 | 3;
  *(byte*) 0xDD02 = TMP1;
  // VIC-II BANK
  TMP1 = *(byte*) 0xDD00;
  TMP1 = TMP1 & 252;
  *(byte*) 0xDD00 = TMP1;
  // BITMAP LOCATION, RELATIVE TO VIC-II BANK
  *(byte*)0xD018 = 0x48;
  // BITMAP MODE (POKE53265,PEEK(53265)OR32)
  TMP1 = *(byte*) 0xD011;
  TMP1 = TMP1 | 32;
  *(byte*) 0xD011 = TMP1;
  video_clear();
}

Drugą optymalizacją było zrezygnowanie z czyszczenia całej bitmapy w każdej klatce. Program zapamiętuje rzutowane wierzchołki z poprzedniej klatki i zamalowuje kolorem tła tylko te linie.

Mapa pamięci

Wiedza o tym, co gdzie się znajduje, to na tej maszynie połowa pracy. Układ, na którym ostatecznie stanąłem w programie cc65:

Adres Co
$0400–$07FF ekran tekstowy
$0800– kod programu (nagłówek BASIC-a + skompilowany C)
do $CFFF wolny RAM; sterta rośnie w górę, stos C rośnie w dół od $CFFF
$D000–$D3FF rejestry VIC-II
$D400–$D7FF SID
$DC00–$DDFF CIA 1 i CIA 2
$E000–$FFFF KERNAL ROM

Stan klawiatury odczytywany jest ze strony zerowej pod $00CB zamiast przez KERNAL.

Modele z Blendera

Ręczne wpisywanie list wierzchołków szybko się nudzi. Mały skrypt w Rubym czyta plik Wavefront OBJ wyeksportowany z Blendera i wypisuje tablice C gotowe do wklejenia do main.c:

Bash
cd models
ruby 0_parser.rb cube.obj

Liczba wierzchołków ma ogromne znaczenie: na C64 obliczenia macierzowe zajmują znacznie więcej czasu niż rysowanie linii.

Asembler 6502 z DASM #

Część w asemblerze to początek trzeciego portu. Używa makr DASM opakowujących procedury z ROM-u KERNAL-a i BASIC-a (wypisanie łańcucha, ustawienie kursora, czyszczenie ekranu, wypisanie liczby) i jest uruchamiana z BASIC-a przez SYS 4096, ponieważ kod jest umieszczony pod $1000:

Text
   processor 6502
   org    $1000
R_PRINT_STRING = $AB1E
R_SET_CURSOR   = $E50C
R_CLEAR_SCREEN = $E544
  mac puts
    lda #<{1}
    ldy #>{1}
    jsr R_PRINT_STRING
  endm

Wynik działania programu w asemblerze DASM w VICE

Czego się z tego nauczyłem #

  • Ten sam algorytm w BASIC-u, C i asemblerze to najczytelniejsza znana mi lekcja „kosztu abstrakcji”. Kod z cc65 jest dużo większy niż ręcznie pisany asembler, a interpretowany BASIC jest dużo wolniejszy od obu.
  • Na sprzęcie 8-bitowym rysowanie jest tanie, a matematyka droga. Arytmetyka wyłącznie całkowitoliczbowa, wstępnie obliczone wartości i przerysowywanie tylko tego, co się zmieniło, liczą się bardziej niż cokolwiek innego.
  • Rzut 3D z tego projektu trafił potem do sektora rozruchowego x86, na ESP8266, a później do OpenCL w Simple High Performance Computing.

Źródło: retro-lab/c64. Zobacz też przegląd moich praktycznych projektów.

Wersja Markdown

Wszystkie treści na tej stronie są udostępniane na licencji Uznanie autorstwa – Użycie niekomercyjne – Bez utworów zależnych 4.0 Międzynarodowe (CC BY-NC-ND 4.0). Cytuj jako: Michał Sobczak, „Commodore 64: renowacja i grafika 3D w BASIC-u, C i asemblerze”, 25 października 2026, https://michalasobczak.pl/pl/posts/commodore-64-restoration-and-3d-in-basic-c-and-assembly.html