---
title: "Commodore 64: renowacja i grafika 3D w BASIC-u, C i asemblerze"
url: https://michalasobczak.pl/pl/posts/commodore-64-restoration-and-3d-in-basic-c-and-assembly.html
author: "Michał Sobczak"
description: "Odnowiłem Commodore 64 z oryginalnym pudełkiem i trzy razy napisałem na niego ten sam renderer 3D wireframe: w BASIC-u 2.0, w C z cc65 i w asemblerze 6502 z DASM. Notatki o mapie pamięci, trybie bitmapowym bez TGI i imporcie modeli z Blendera."
published: 2026-10-25T09:30:00.000Z
updated: 2026-10-25T09:30:00.000Z
language: pl
categories: ["Hobby"]
tags: ["Vintage Computing", "3D", "Programming"]
license: CC BY-NC-ND 4.0 (https://creativecommons.org/licenses/by-nc-nd/4.0/)
---
# Commodore 64: renowacja i grafika 3D w BASIC-u, C i asemblerze

> **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](https://github.com/michalasobczak/retro-lab/tree/master/c64) w moim repozytorium [retro-lab](https://github.com/michalasobczak/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ą](https://michalasobczak.pl/media/projects/c64-restoration-IMG_20180612_114706.jpg)

![Tylne porty obudowy C64 podczas czyszczenia](https://michalasobczak.pl/media/projects/c64-restoration-IMG_20180612_132114.jpg)

![Klawiatura z wyczyszczonymi klawiszami założonymi z powrotem na ramę](https://michalasobczak.pl/media/projects/c64-restoration-IMG_20180612_153355.jpg)

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](https://michalasobczak.pl/media/projects/c64-restoration-IMG_20180616_204750.jpg)

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](https://michalasobczak.pl/posts/generic-3d-engine-in-javascript-ported-to-esp8266.html), 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](https://michalasobczak.pl/media/projects/c64-basic-typing.png)

![Rzutowane współrzędne i czas obliczeń dla każdego wierzchołka wypisane przez wersję w BASIC-u](https://michalasobczak.pl/media/projects/c64-basic-3d.png)

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](https://michalasobczak.pl/media/projects/c64-cc65-3d.png)

#### 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](https://michalasobczak.pl/media/projects/c64-asm.png)

## 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](https://michalasobczak.pl/posts/x86-bootloader-2d-and-3d-graphics-in-nasm.html), na [ESP8266](https://michalasobczak.pl/posts/generic-3d-engine-in-javascript-ported-to-esp8266.html), a później do OpenCL w [Simple High Performance Computing](https://michalasobczak.pl/posts/simple-high-performance-computing.html).

Źródło: [retro-lab/c64](https://github.com/michalasobczak/retro-lab/tree/master/c64). Zobacz też [przegląd moich praktycznych projektów](https://michalasobczak.pl/posts/hands-on-projects-on-github.html).
