Dua Z80s per satu mesin: bagaimana perbedaan mesin arcade 8-bit dari komputer di rumah?

Apa yang saya pelajari tentang mesin arcade Jack Bom dalam proses menciptakan emulatornya




Baru-baru ini saya menulis sebuah emulator mesin Bomb Jack kecil, terutama untuk mencari tahu bagaimana mesin arcade 8-bit pertama ini berbeda dalam desain dari komputer rumahan 8-bit.

Seperti yang saya ketahui kemudian, pertemuan di sebuah pameran musim panas di kota kelahiran saya dengan mesin-mesin arcade seperti Bomb Jack adalah salah satu momen yang mengubah nasib saya. Pada hari musim panas yang normal, setelah menghabiskan seluruh persediaan koin saya di mesin arcade, saya kembali ke rumah, dan kepala saya dipenuhi dengan bunga dan efek suara. Saya mencoba memahami bagaimana permainan ini bekerja. Dan kemudian sampai akhir tahun saya menghabiskan seluruh waktu saya sepulang sekolah untuk membuat salinan game arcade yang agak pudar di komputer rumah saya. Saya seperti penggemar kultus kargo dari pulau-pulau di Samudra Pasifik, yang ingin membuat stasiun radio militer Amerika dari tongkat.

Pada awalnya saya berpikir tentang ide membuat emulator Pengo , karena otak remaja saya jauh lebih terkesan dengan permainan ini daripada Bomb Jack (omong-omong, ini adalah versi Pengo kargo-kultus saya). Tetapi peralatan arcade Pengo akan membutuhkan penciptaan emulator chip baru untuk audio dan video, dan untuk Bomb Jack ada cukup banyak bagian yang sudah saya miliki (Z80 sebagai CPU dan AY-3-8910 untuk suara), jadi saya yang pertama kali mengambil Bomb Jack.

Selain itu, Bomb Jack adalah kesempatan besar untuk akhirnya menambahkan dukungan NMI (non-maskable interrupt) ke emulator Z80 saya. Tidak satu pun dari mesin berbasis Z80 yang sebelumnya saya tiru menggunakan NMI, dan oleh karena itu tidak ada gunanya menciptakan kembali fungsi ini - saya masih tidak dapat memeriksa operasinya.

Jika Anda tidak tahu apa itu Bomb Jack, maka game ini terlihat seperti ini (tidak yakin apakah saya memilih rasio aspek yang tepat):

Tangkapan Layar Bom Jack


Versi emulator di WebAssembly dapat ditemukan di sini:

https://floooh.imtqy.com/tiny8bit/bombjack.html

Setelah prosedur pemuatan selesai dan tabel skor tinggi muncul, tekan 1 untuk menjatuhkan koin, lalu Enter (atau tombol lainnya kecuali panah dan spasi) untuk memulai permainan.

Di dalam gim, gunakan tombol panah untuk mengubah arah dan bilah spasi untuk melompat. Saat berada di udara, tekan bilah spasi untuk memperlambat jatuh.

Kode sumber ada di sini:

https://github.com/floooh/chips-test/blob/master/examples/sokol/bombjack.c

Ia menggunakan chip header untuk memberikan emulasi Z80 dan AY-3-8910, serta header sokol sebagai pembungkus lintas platform (untuk memasuki aplikasi, rendering, input dan suara).

Langkah 1: Penelitian


"Penelitian" adalah kata yang terlalu besar: Saya baru saja menekan "spesifikasi perangkat keras arcade Bombjack" Google.

Dibandingkan dengan komputer rumah tahun 80-an yang populer (atau bahkan komputer Eropa Timur yang misterius, yang sering masih memiliki komunitas aktif), ada sangat sedikit informasi tentang Bom Jack di Internet.

Saya menemukan dua informasi yang sangat penting: diagram rangkaian mesin dan, tentu saja, kode sumber emulator MAME .

Ada juga proyek yang mengimplementasikan Bom Jack pada FGPA , dari sumber VHDL yang saya berhasil mencari tahu detail yang tidak ada dalam diagram rangkaian.

Memahami kode sumber MAME akan sulit, karena emulator mesin arcade biasanya hanya sekelompok makro yang menggambarkan bagaimana berbagai bagian peralatan berinteraksi, tetapi tidak ada banyak kode sumber .

Namun demikian, deskripsi makro dari peralatan, dan terutama komentar, ternyata masih sangat berguna untuk memahami pengoperasian perangkat keras, dan di mana mereka menjadi terlalu samar (misalnya, bagian pada decoding video ), percobaan dan kesalahan sudah cukup, serta studi rinci tentang konsep.

Ikhtisar Perangkat Keras


Yang paling menarik tentang perangkat keras Bomb Jack adalah bahwa sebenarnya dua komputer terhubung satu sama lain dengan pita listrik: ada papan utama dengan CPU Z80 dan peralatan decoding video dan kartu suara terpisah dengan CPU Z80 sendiri dan tiga (ya, tiga!) chip suara AY-3-8910.

Peralatan pengodean video tidak diimplementasikan sebagai sirkuit terpadu - ini hanya banyak chip untuk keperluan umum kecil (sirkuit mereka membutuhkan 6 dari 10 halaman diagram sirkuit perangkat). Ketika membuat emulator, saya memutuskan untuk mengambil jalan pendek: alih-alih meniru bagian tertentu dari peralatan decoding video, saya hanya meniru perilakunya, menciptakan output yang sesuai dari data input dan tidak benar-benar mengkhawatirkan tentang bagaimana peralatan itu sendiri bekerja di tengah.

Solusi yang disederhanakan seperti ini sangat cocok untuk mesin arcade yang terpisah, yang dirancang untuk menjalankan hanya satu program. Jika permainan dimulai dan bekerja dengan benar, maka emulasi dapat dianggap "cukup baik."

Selain itu, pendekatan yang disederhanakan ini merupakan perbedaan penting dari emulasi sebagian besar komputer di rumah: beberapa game memerlukan emulasi yang lebih akurat daripada yang lain, misalnya, mesin seperti C64 atau Amstrad CPC perlu emulasi yang sangat tepat hingga siklus clock, sehingga sistem video dari beberapa game dan grafik demo bekerja dengan benar.

Ini juga berarti bahwa CPU saya yang sudah jadi dan emulator chip suara sebenarnya merupakan pekerjaan tambahan untuk Bomb Jack, misalnya, bekerja dengan CPU Z80 dengan implementasi fraksinasi siklus mesin adalah pembunuhan yang berlebihan, fragmentasi yang lebih sederhana dan lebih cepat pada level instruksi sudah cukup.

Papan utama


Biasanya hal pertama yang saya coba cari tahu ketika menulis emulator baru adalah skema alokasi memori (di mana area ROM dan RAM, memori video dan alamat khusus atau port input / output).

Hanya ada satu chip "menarik" di papan utama Bomb Jack - CPU Z80 yang beroperasi pada 4 MHz. Semua ruang yang tersisa di papan utama ditempati oleh peralatan decoding video (dengan pengecualian sepasang RAM dan chip ROM).

Ruang alamat 16-bit adalah sebagai berikut:

  • 0000..7FFF : ROM 32 KB
  • 8000..8FFF : 4 KB dari RAM tujuan umum
  • 9000..93FF : Memori video 1 KB
  • 9400..97FF : RAM warna 1 KB
  • 9820..987F : 96 byte RAM sprite
  • 9C00..9CFF : 256 palet warna RAM
  • 9E00, B000..B005, B800 : port input-output
  • C000..DFFF : ROM 8KB

Area port I / O adalah sebagai berikut. Beberapa port hanya untuk menulis, beberapa hanya untuk read-only, dan beberapa memiliki fungsi yang berbeda saat membaca dan menulis kepada mereka:

  • 9E00 : tulis: nomor gambar latar belakang saat ini, baca: -
  • B000 : baca: status joystick pemain 1, tulis: aktifkan / nonaktifkan topeng NMI
  • B001 : baca: status pemain 2 joystick, tulis: -
  • B002 : baca: koin dan tombol Mulai, tulis: -
  • B003 : baca: pengawas CPU, tulis: ???
  • B004 : baca: sakelar dip 1, tulis: sakelar layar
  • B005 : baca: sakelar dip 2, tulis: -
  • B800 : tulis: perintah kartu suara, baca: -

Berikut ini layak disebutkan di sini:

  • Perangkat ini memiliki BANYAK ROM (40 Kbytes), dan sangat sedikit RAM (sekitar 7 Kbytes, dan hanya 4 Kbytes di antaranya adalah "RAM untuk keperluan umum")
  • Hanya 2 Kbytes yang dialokasikan untuk RAM tampilan, dibagi menjadi dua fragmen 1 Kbyte, yang tampaknya sangat kecil untuk tampilan penuh warna 256x256, di mana, tampaknya, warna-warna tersebut diatur pixel demi pixel
  • Ini adalah sistem I / O dalam skema alokasi memori!

I / O dalam skema alokasi memori sedikit tidak biasa untuk mesin Z80, karena salah satu keunggulan Z80 adalah ruang alamat 16-bit yang terpisah untuk I / O. Ini dilakukan untuk menghemat ruang alamat memori yang berharga. I / O dalam skema alokasi memori biasanya ditemukan di komputer dengan prosesor 6502.

Melihat diagram sirkuit menegaskan ini: pin IORQ tidak terdeteksi pada CPU papan utama, hanya pin MREQ yang terhubung (yang digunakan untuk menginisialisasi membaca atau menulis ke memori):

Bom jack iorq

Ini berarti bahwa kita tidak perlu khawatir tentang permintaan I / O untuk fungsi pengatur waktu CPU dari papan utama di emulator, tetapi hanya berurusan dengan permintaan memori.

Setelah mempelajari diagram sirkuit, saya menemukan detail menarik lainnya tentang CPU dari papan utama:

Hanya pin NMI yang terhubung, sedangkan pin INT selalu mempertahankan sinyal clock tingkat tinggi / tetap tidak aktif (ini berarti bahwa interupsi bertopeng "biasa" tidak dieksekusi, dan hanya interupsi tanpa topeng yang terjadi):

Bom jack iorq

Ini juga cukup tidak biasa untuk mobil dengan Z80. Di semua komputer rumahan berbasis Z80 yang saya gunakan untuk menangani, yang terjadi adalah sebaliknya - mereka hanya menggunakan interupsi maskable dan tidak pernah menggunakan yang non-maskable. Z80 interupsi bertopeng adalah peningkatan yang sangat fleksibel dan serius dibandingkan dengan sistem interupsi primitif dari "ayah tidak sah" -nya - Intel 8080, atau pesaingnya - MOS 6502. Tetapi peningkatan fleksibilitas ini juga lebih sulit untuk diterapkan dalam peralatan (kecuali sebagai sumber interupsi chip lain dari keluarga Z80 digunakan, di mana sudah ada protokol interupsi built-in yang kompleks ketika dihubungkan dengan bus).

Oh well, cukup detail tentang peralatannya, ayo beralih ke emulator!

Prosedur boot


Langkah selanjutnya setelah menentukan konfigurasi memori adalah menyambungkan CPU yang diemulasi ke skema alokasi memori yang diemulasi, merekam beberapa jenis visualisasi dari isi memori video, dan memulai siklus CPU.

Anehnya, pendekatan kasar semacam itu sering cukup untuk melalui prosedur pemuatan dan menampilkan sesuatu di layar. Saat merancang emulator Bom Jack, saya hanya mengambil konten memori video 1KB dalam rentang dari 0x9000 hingga 0x93FF sebagai matriks 32x32 byte. Ketika byte adalah 0, saya membuat blok piksel hitam 8x8, dan sebaliknya - blok piksel putih.

Lalu saya hanya menjalankan CPU yang ditiru dan berharap yang terbaik. Lihat! Beberapa jenis gambar yang dapat dibaca muncul:

Booting jack bom 1

Booting jack bom 2

Gambar atas terlihat seperti layar pengujian perangkat keras saat boot, dan bagian bawah terlihat seperti layar catatan skor yang muncul setelah prosedur boot selesai:

Booting jack bom 3

Booting jack bom 4

... tetapi diputar 90 derajat (yang logis, karena layar mesin arcade sering dalam orientasi "potret" vertikal).

Hebat, awalnya menjanjikan!

Langkah selanjutnya adalah mencari cara mengubah blok putih ini menjadi piksel warna ... (dan ini adalah langkah besar, detailnya dijelaskan di bawah ini pada bagian tentang decoding video).

Pada awalnya semuanya berjalan cukup cepat, pada layar tes, piksel dan warna ditampilkan selama pemuatan (kemudian saya perhatikan bahwa decoding warna benar-benar salah, namun ...):

Booting jack bom 5

Tapi ketika layar rekam muncul, saya mendapat layar hitam. Meretas warna latar belakang sehingga "bukan hitam", saya menemukan bahwa piksel diberikan, tetapi seluruh palet warna hitam. Hmm ...

Booting jack bom 5

Setelah melihat layar ini selama beberapa menit, saya ingat bahwa beberapa warna pada layar highscore adalah animasi, dan ketika ada animasi, harus ada semacam pengatur waktu. Sumber logis waktu dalam konfigurasi peralatan ini adalah sinyal tampilan VSYNC, dan VSYNC terhubung ke pin NMI CPU (atau lebih tepatnya, bukan VSYNC, tetapi VBLANK, yang merupakan momen singkat antara sinyal VSYNC dan berkas sinar katoda yang bergerak ke sudut kiri atas).

Dan saya belum menerapkan semua ini ...

Malam berikutnya, ketika saya menambahkan versi pertama pemrosesan NMI ke emulasi Z80 dan menghubungkannya ke counter pertama vsync / vblank dalam fungsi pengatur waktu CPU papan utama, banyak hal tiba-tiba mulai terjadi!

Pertama, warna muncul di layar rekaman, dan beberapa di antaranya dianimasikan:

Bom Jack NMI 1

Setelah beberapa detik, sesuatu yang lebih menarik dimulai! Skor tinggi menghilang dan visualisasi aneh dari peta pertama ditampilkan. Sudah jelas bahwa ini adalah mode demo dari mesin arcade untuk menarik perhatian - saya melihat beberapa bom dengan animasi warna yang menghilang ketika Bom Jack imajiner melompat pada peta yang mengumpulkan bom-bom ini:

Bom Jack NMI 2

Warnanya masih benar-benar salah, namun PROGRESS!

Ini adalah waktu yang tepat untuk melakukan decoding video lainnya:

Besi video


Pada pandangan pertama, peralatan pemrosesan video di Bomb Jack tampak sangat kuat untuk mesin 8-bit dari tahun 1984: meskipun resolusi hanya 256x256 piksel, secara bersamaan dapat menampilkan 128 (dari 4096) warna, dan membuat hingga 24 sprite perangkat keras (ukuran 16x16) atau 32x32) dengan warna piksel demi piksel.

Komputer rumahan 8-bit pada waktu itu memiliki resolusi layar yang kira-kira sama, tetapi mereka memiliki banyak batasan warna. Pembatasan ini sangat jelas terlihat ketika membandingkan versi Bomb Jack untuk ZX Spectrum dan Amstrad CPC dengan versi untuk mesin arcade:

Versi untuk ZX Spectrum memiliki resolusi piksel yang cukup baik (256x192), tetapi sangat sedikit warna, dan itu menderita dari efek "konflik warna" khas Spectrum (meskipun pengembang melakukan yang terbaik untuk membuat ini tidak begitu terlihat):

Jack bom zx

Versi untuk Amstrad CPC lebih penuh warna, tetapi untuk mendapatkan lebih banyak warna, pengembang harus beralih ke mode tampilan resolusi rendah (160x200). Sebagai akibatnya, Jack dan monster-monster berubah menjadi sekelompok piksel yang tak terbaca:

Bom jack cpc

Bandingkan ini dengan versi untuk mesin arcade, yang memiliki resolusi piksel yang sama dengan ZX Spectrum, tetapi dengan lebih banyak warna dan peningkatan resolusi warna piksel-demi-piksel:

Bom jack arcade

Yang menarik di sini adalah bahwa versi arcade memiliki grafik yang lebih baik, bukan karena ia bekerja pada perangkat keras yang lebih kuat (memiliki lebih banyak ROM untuk menyimpan lebih banyak data grafik, tetapi "daya komputasi" hampir sama), tetapi karena pengembang perangkat dapat fokus pada pembuatan mesin khusus untuk satu jenis permainan tertentu dan mereka tidak perlu membuat komputer rumah universal untuk penggunaan umum.

Berikut cara kerja perangkat keras layar (setidaknya dalam interpretasi tingkat tinggi saya):

Tampilan tiga lapis


Sinyal video Bomb Jack yang sudah jadi digabungkan dari tiga lapisan: lapisan latar belakang, lapisan depan dan lapisan sprite.

Sistem lapisan seperti itu memiliki dua keunggulan utama:

  • Ini mengimplementasikan kompresi gambar perangkat keras yang agak rumit untuk menghasilkan gambar penuh warna "resolusi tinggi" dari sejumlah kecil data.
  • Ini secara signifikan mengurangi jumlah pekerjaan CPU yang diperlukan untuk memperbarui elemen layar dinamis (bahkan pada frekuensi 4 MHz, CPU 8-bit tidak memiliki daya yang cukup untuk memindahkan sejumlah objek pada layar 256x256 dengan frekuensi 60 Hz)

Video besi sangat berbeda dari yang saya lihat di komputer rumahan 8-bit, tetapi di kelas tambahan MAME umum untuk jenis peralatan ini diterapkan, jadi saya dapat berasumsi bahwa itu sangat umum di mesin arcade.

Lapisan latar belakang


Lapisan latar belakang dapat membuat 1 dari 5 gambar latar belakang tertanam dalam ROM. Gambar latar belakang dipilih dengan menulis nilai dari 1 hingga 5 di alamat 0x9E00 (tampaknya nilai 0 khusus dan menjadikan latar belakang yang benar-benar hitam).

Bahkan, tampaknya peralatan tersebut mampu membuat 7 gambar yang berbeda, tetapi hanya 5 yang digunakan dalam permainan. Diam-diam, saya berharap untuk menemukan data gambar yang sebelumnya tidak terdeteksi dalam ROM. Namun sayang, mereka tidak ada di sana (ya, mungkin saya bukan yang pertama mencari mereka di sana).

Inilah yang tampak seperti lapisan latar belakang peta pertama tanpa dua lapisan lainnya:

Latar belakang jack bom

Lapisan latar belakang dirakit dari ubin 16x16 piksel.

Keuntungan membangun gambar latar belakang dari ubin adalah ubin yang sama dapat digunakan beberapa kali, sehingga lebih sedikit data yang dapat disimpan dalam ROM. Perhatikan bahwa langit biru, bagian piramida, dan pasir di bawah piramida menggunakan ubin yang sama:

Ubin latar belakang jack bom

Untuk menghemat memori, peralatan lapisan latar menerapkan trik lain - ubin dapat diputar secara horizontal. Saya hampir melewatkan ini dalam implementasi saya karena saya berasumsi bahwa perangkat lunak tidak menggunakan fungsi perangkat keras ini, tetapi melihat bug kecil di latar belakang kartu ketiga:

Latar belakang jack bom 3

Saya menggunakan trik yang sama pada peta kelima, tetapi di sini agak sulit untuk diperhatikan jika Anda tidak tahu apa yang harus dicari:

Latar belakang jack bom 5


Lapisan depan:


Di atas lapisan latar belakang adalah "lapisan depan", yang menjadikan semua bagian layar tetap, yang harus diperbarui oleh CPU (terutama teks, platform, dan bom). Tata letak dibaca dari RAM (dari fragmen RAM 1-KB dan RAM warna 1-KB).

Inilah tampilan lapisan depan terisolasi dari peta pertama:

Latar depan jack bom

Lapisan depan juga terdiri dari ubin (serta latar belakang), tetapi menggunakan ubin 8x8 yang lebih kecil:

Ubin latar belakang jack bom

Keuntungan utama membagi latar belakang dan depan menjadi lapisan-lapisan terpisah adalah bahwa CPU tidak perlu khawatir tentang menyimpan dan mengembalikan piksel latar belakang saat membuat atau menghapus elemen depan.

Lapisan sprite


Akhirnya, sprite perangkat keras ditampilkan di atas lapisan depan. Segala sesuatu yang bergerak di sekitar layar diimplementasikan dalam sprite. Peralatan Bom Jack dapat membuat hingga 24 sprite, dan setiap sprite dapat memiliki ukuran 16x16 atau 32x32 piksel. Dalam hal ini, sprite dapat diposisikan dengan akurasi pixel-by-pixel:

Sprite jack bom


8x8 decoder ubin


Inti dari peralatan decoding video adalah palet warna dengan 128 elemen dan decoder ubin 8x8 piksel. Tugas decoder ubin adalah untuk menghasilkan indeks palet warna 7-bit untuk masing-masing 64 piksel ubin.

Ubin 8x8 ini adalah blok bangunan untuk semua yang ada di layar - ubin latar 16x16, ubin lapisan depan 8x8, dan sprite perangkat keras 16x16 atau 32x32.

Berikut adalah diagram blok decoder ubin 8x8 ini untuk menampilkan lapisan depan (seperti yang saya mengerti):

Dekode Bom Jack Tile

Penjelasan diagram blok top-down:

  • Proses decoding dimulai di atas dengan membaca byte "kode ubin" dari memori video (disusun sebagai matriks kode ubin 32x32) dan byte terpisah dari RAM warna (juga matriks 32x32). Mendapatkan kode ubin dan warna dari memori video hanya terjadi untuk lapisan depan, tetapi saya menambahkannya untuk membuat gambar secara keseluruhan lebih dimengerti. Decoder ubin 8x8 itu sendiri hanya membutuhkan ubin dan kode warna pada input.
  • . ( ). , , ( ).
  • 8 , 8 ( ). , , 8x8 24 (3 ).
  • 64 7- . 3 , 4 — . , , 16 «», 8 . 8 .
  • 7- , , 12- RGB- (4 ). ( , , ; , ).

Ini adalah skema decoding ubin umum yang digunakan oleh masing-masing dari tiga lapisan tampilan, tetapi decoding dari setiap lapisan sedikit berbeda:

  • Lapisan depan sebenarnya dapat membuat 512 ubin 8x8 berbeda. Ini membutuhkan kode ubin 9-bit, tetapi memori video hanya menyediakan 8 bit per ubin. Bit kesembilan "dipinjam" dari bit kelima dari nilai warna (karena hanya 4 bit dari nilai warna yang digunakan untuk membangun indeks palet warna, 4 bit lagi tetap untuk keperluan lain). Jika semua 3 bit dari lapisan bit ubin 8x8 sama dengan nol, maka piksel depan dianggap transparan, dan piksel latar belakang “bersinar melalui” itu.
  • 16x16, 16x16=256 256 (512 ). , 16x16 8x8, . , ; «» : 7 , .
  • 16x16 32x32 , 4 16 8x8 . , 16x16 96 , 32x32 — 384 . , 3 , .

Untuk lebih memahami bagaimana lapisan bit ubin terlihat, saya menulis sebuah program C kecil yang mengubah ubin ROM ke file PNG (3 bit per piksel dikonversi ke 8 level abu-abu).

Berikut ini menunjukkan ubin ROM dari lapisan depan. Kami melihat angka dan data font teks, ubin platform, bom (dibagi dua), bagian logo dari screensaver Bom Jack, dan jumlah pengganda skor yang muncul di bagian atas layar (omong-omong, semuanya diputar 90 derajat, karena seluruh layar juga diputar ):

ROM Bom Jack Foreground Tile

Selanjutnya, pertimbangkan ubin ROM latar belakang. Itu tidak terlihat sangat jelas, karena apa yang kami amati sebenarnya decoding 16x16 ubin menjadi 8x8 ubin. Setiap ubin 16x16 dibuat dari empat ubin 8x8 yang berdekatan. Tetapi Anda dapat mengenali bagian dari kuil Yunani dari peta 2, kastil dari peta 3 dan gedung pencakar langit dari peta 4.

ROM Latar Belakang Tile Bom Jack

Dan akhirnya, ubin sprite ROM. Sprite 16x16 menempati bagian atas, dan 32x32 sprite menempati bagian bawah.

ROM Bom Jack Sprite Tile

Retasan menarik screen saver Bomb Jack adalah bahwa logo tersebut dirangkai dari ubin depan dan sprite. Saya pikir para pengembang kehabisan ROM ubin depan, tetapi ada sedikit ruang tersisa di ROM sprite:

Bom Jack Splash 1

Bom Jack Splash 2

Bom Jack Splash 3

Peralatan sprite


Peralatan sprite Bomb Jack sangat kuat dibandingkan dengan apa yang digunakan di komputer rumah pada waktu itu:

  • Itu bisa membuat hingga 24 sprite perangkat keras. Tampaknya tidak ada batasan jumlah sprite per baris pemindaian.
  • Sprite dapat memiliki ukuran 16x16 piksel atau 32x32 piksel
  • Setiap sprite dapat memilih satu dari 16 slot 8 warna dalam palet warna yang sama
  • Sprite memiliki resolusi warna pixel-by-pixel.
  • Setiap sprite dapat diputar secara vertikal atau horizontal
  • Setiap sprite dapat memilih salah satu dari 128 gambar sprite yang di-flash dalam ROM.

Saat mendekode piksel dan sprite dari sistem sprite, ubin dasar 8x8 yang sama digunakan seperti pada latar belakang dan lapisan depan.

Atribut sprite terletak di kisaran alamat dari 0x9820 hingga 0x987F - 96 byte, 4 byte per sprite. Sejauh yang saya lihat, area ini hanya untuk merekam; setidaknya CPU tidak melakukan akses baca ke rentang memori ini.

Setiap sprite dijelaskan oleh 4 byte:

  • Byte 0 :
    • Bit 7 : jika diatur, maka ini adalah sprite 32x32, jika tidak 16x16
    • Bit 6..0 : 7 bit untuk mengatur kode ubin sprite yang digunakan untuk mencari lapisan bit dari gambar sprite di ubin ROM.
  • Byte 1 :
    • Bit 7 : jika diatur, sprite terbalik secara horizontal
    • Bit 6 : jika diatur, sprite dibalik secara vertikal
    • Bit 3..0 : 4 bit untuk mengatur nilai warna untuk decoder ubin

  • Byte 2 : Posisi sprite sumbu-X pada layar
  • Byte 3 : posisi sprite pada layar di sepanjang sumbu Y

Tidak jelas apa yang dilakukan bit 4 dan 5 dari byte 1, komentar di MAME mengatakan ini:

e ? (, )
f ? (, (B)?)


Port Memori I / O


Beberapa catatan tentang port input / output papan utama. Seperti yang dinyatakan di atas, port I / O terlihat seperti ini:

  • 9E00 : tulis: nomor gambar latar belakang saat ini, baca: -
  • B000 : baca: status joystick pemain 1, tulis: aktifkan / nonaktifkan topeng NMI
  • B001 : baca: status pemain 2 joystick, tulis: -
  • B002 : baca: koin dan tombol Mulai, tulis: -
  • B003 : baca: pengawas CPU, tulis: ???
  • B004 : baca: sakelar dip 1, tulis: sakelar layar
  • B005 : baca: sakelar dip 2, tulis: -
  • B800 : tulis: perintah kartu suara, baca: -

Alamat 0x9E00 (pemilihan gambar latar belakang) yang telah kita pertimbangkan di atas, dan alamat 0xB800 (kartu suara perintah) akan kita bahas di bagian selanjutnya. Tetap alamat dari 0xB000 hingga 0xB005:

Membaca dari alamat 0xB000 dan 0xB001 mengembalikan keadaan saat ini dari dua joystick. Set byte yang menunjukkan sakelar joystick tertutup:

  • bit 0 : arah yang benar
  • bit 1 : arah kiri
  • bit 2 : arah ke atas
  • bit 3 : arah bawah
  • bit 4 : tombol melompat ditekan

3 bit yang tersisa diabaikan.

Membaca dari 0xB002 mengembalikan status akseptor koin dan tombol Mulai:

  • bit 0 : pemain 1 koin dilemparkan
  • bit 1 : pemain 2 koin dilemparkan
  • bit 2 : tombol mulai pemain 1
  • bit 3 : tombol mulai 2 pemain

Membaca dari alamat 0xB004 dan 0xB005 mengembalikan status dip switch yang digunakan untuk mengkonfigurasi perilaku mesin arcade:

  • B004 :
    • bit 0,1 : berapa banyak "permainan" yang diberikan untuk satu koin (1, 2, 3 atau 5)
    • bit 2,3 : sama untuk pemain 2
    • bits 4,5 : berapa banyak nyawa per game (3, 4, 5 atau 2)
    • bit 6 : lokasi mesin arcade: "meja koktail" atau "vertikal".
    • bit 7 : apakah akan memutar suara dalam mode siaga
  • B005 :
    • bit 3.4 : tingkat kesulitan 1 (kecepatan burung)
    • bits 5,6 : kesulitan 2 (jumlah dan kecepatan musuh)
    • bit 7 : frekuensi kemunculan koin tertentu

Akhirnya, membaca dari alamat B003 mengimplementasikan pengawas perangkat lunak. CPU harus sering membaca dari alamat ini, jika tidak mesin arcade akan melakukan reset perangkat keras. Jika karena alasan tertentu game mogok, peralatan akan secara otomatis reboot.

Anda dapat menulis ke beberapa alamat port I / O:

  • B000 : apakah akan menghasilkan NMI selama vblank; tampaknya dinonaktifkan hanya selama prosedur booting
  • B004 : balikkan seluruh layar; Saya belum pernah bertemu penggunaan fungsi ini, tapi saya punya teori tentang hal itu (lihat di bawah)

Fungsinya membalik layar agak membingungkan, karena ketika bermain game, saya tidak pernah melihat penggunaannya. Namun, saya punya firasat tentang apa yang dia lakukan, tetapi untuk mengkonfirmasinya, Anda perlu menulis kode. Ketika mesin arcade berada dalam konfigurasi "meja koktail", dua pemain duduk saling berhadapan. Oleh karena itu, saya menyarankan bahwa ketika permainan beralih dari pemain 1 ke pemain 2, fungsi ini membalik layar. Namun, saya belum menerapkan mode dua pemain di emulator.

Papan suara


Kartu suara itu sendiri adalah komputer berfitur lengkap dengan CPU Z80 (beroperasi pada frekuensi 3 MHz), tiga chip suara (AY-38910 beroperasi pada frekuensi 1,5 MHz), serta RAM dan ROM. Skema alokasi memori kartu suara terlihat cukup sederhana:

  • 0000..2000 : 8 Kbytes ROM
  • 4000..4400 : 1 KB RAM
  • 6000 : perintah suara dari papan utama

Karena tidak ada yang menarik dalam skema alokasi memori di atas alamat 0x8000, kontak alamat paling atas dari CPU bahkan tidak terhubung:

Bom Jack Sound A15

Alamat khusus 0x6000 adalah port I / O (kait 8-bit) yang terletak di memori, yang tidak sesuai dengan RAM nyata. Ini adalah port yang sama yang terletak di papan utama di 0xB800. Ini adalah saluran komunikasi antara kartu utama dan kartu suara.

Tiga chip suara dikendalikan oleh instruksi output Z80 ini, bukan melalui port memori. AY-3-8910 hanya memiliki dua port I / O terbuka, yang pertama digunakan untuk menyimpan nomor register, dan yang kedua digunakan untuk menulis atau membaca isi register yang ditentukan oleh port pertama.

Rangkaian I / O adalah sebagai berikut:

  • 0x00 : chip suara pertama: daftar pilihan
  • 0x01 : chip suara pertama: akses ke register yang dipilih
  • 0x10 : chip suara kedua: daftar pilihan
  • 0x11 : chip suara kedua: akses ke register yang dipilih
  • 0x80 : chip suara ketiga: pilihan register
  • 0x81 : chip suara ketiga: akses ke register yang dipilih

Beberapa kata tentang chip suara AY-3-8910:

Ini adalah perangkat yang cukup standar, sangat populer di komputer rumah pada waktu itu (misalnya, di Amstrad CPC, ZX Spectrum 128, di komputer MSX dan banyak lainnya). AY-3-8910 melahirkan banyak variasi dan klon (misalnya, Yamaha YM2149, yang dengan sendirinya menjadi dasar dari seluruh keluarga chip suara yang lebih kuat).

AY-3-8910 memiliki 3 saluran sinyal persegi panjang, satu generator derau yang dapat dicampur dengan tiga saluran, dan satu generator amplop. Karena hanya ada satu generator amplop untuk ketiga saluran, itu tidak terlalu berguna, dan sebagian besar permainan menggunakan CPU untuk memodulasi nada dan volume.

Ini berarti bahwa chip AY-3-8910 memerlukan lebih banyak intervensi CPU untuk membuat suara berkualitas tinggi (tidak seperti chip SID yang lebih berdiri sendiri, misalnya, di komputer C64).

Sungguh menakjubkan melihat apa yang dapat dilakukan pada tiga chip suara yang cukup sederhana dan CPU yang mengontrolnya. Musik dan efek suara dari Bomb Jack jauh lebih kaya daripada yang saya dengar di kebanyakan game komputer rumahan.

Satu-satunya hal yang sangat menarik dalam kartu suara ini adalah cara ia menerima perintah dari papan utama.

Kait perintah suara


"Sound latch" adalah penyimpanan byte tunggal (latch 8-bit) yang umum untuk kartu utama dan kartu suara. Kait terikat ke alamat 0xB800 di papan utama dan ke alamat 0x6000 pada kartu suara.

Ketika interupsi NMI dinyalakan menggunakan VSYNC, kartu suara melakukan rutinitas layanan interupsi yang sangat sederhana, yang membaca kait perangkat keras, menulisnya ke alamat memori normal dan menetapkan "bit sinyal", yang memberi tahu "loop utama" bahwa perintah suara baru telah diterima:

  ex af,af' ;0066 exx ;0067 ld hl,04390h ;0068 set 0,(hl) ;006b ld a,(06000h) ;006d ld (04391h),a ;0070 exx ;0073 ex af,af' ;0074 retn ;0075 

Metode aktivasi kontak NMI sedikit berbeda dari metode papan utama:

Di papan utama, pin NMI menjadi aktif selama VBLANK run time.

Namun, pada kartu suara, NMI diaktifkan ketika VSYNC dipicu, dan tetap aktif tidak selama VBLANK, tetapi sampai prosedur layanan interupsi membaca data dari kait di 0x6000.

Ketika peralatan mengenali pembacaan dari alamat 0x6000, ia melakukan dua operasi kode keras:

  • isi klip suara diatur ulang ke 0
  • Kontak NMI menjadi tidak aktif

Bahkan, ini adalah penghapusan sederhana dari bouncing kontak, yang tidak memungkinkan satu perintah suara dieksekusi dua kali.

Satu-satunya pertanyaan tetap: seberapa sering papan utama menulis perintah baru (karena cara untuk menerapkan persaingan dua papan tergantung pada ini).

Setelah debugging dengan printf, saya menemukan bahwa papan utama merekam paling banyak satu perintah suara per 60 Hz frame. Ini sangat menyederhanakan struktur "siklus utama" dari emulator.

Masalah kerja bersama dari dua komputer yang dicontoh terpisah yang harus saling bertukar data adalah bahwa emulasi satu komputer hanya efektif jika dapat melakukan banyak siklus pada satu waktu tanpa gangguan.

Misalnya, kasus terburuk adalah:

  • kami menjalankan satu instruksi di komputer 1
  • kami menjalankan satu instruksi di komputer 2
  • ulangi ...

Emulator Z80 saya tidak dioptimalkan untuk keluar dan memasukkan emulasi untuk setiap instruksi, karena dalam hal ini ia harus masuk ke memori dan memuat dari memori keadaan CPU pada awal dan akhir setiap instruksi. Jika CPU dapat memproses banyak instruksi tanpa gangguan, maka Anda dapat menyimpan (sebagian besar) status CPU dalam register dan mengatur ulang status ke memori pada instruksi terakhir.

Yaitu, situasi ideal adalah ini: kami melakukan sistem yang ditiru tanpa gangguan di seluruh kerangka sistem host (untuk CPU dengan frekuensi 4 MHz dan pada 60 Hz ini berarti sekitar 67 ribu siklus per frame, atau di suatu tempat dari 3 ribu hingga 16 ribu instruksi Z80).

Ketika bekerja dengan Bomb Jack, saya perlu memastikan bahwa papan utama tidak merekam perintah baru sebelum kartu suara dapat membaca perintah terakhir. Sebelum saya mengetahui bahwa papan utama mencatat tidak lebih dari satu perintah per frame, saya menganggap perlunya membuat antrian perintah yang kompleks yang akan mencegat rekaman di kait suara papan utama dan menyimpan nomor siklus dan byte perintah dalam antrian.

Kemudian, pada saat kartu suara mengeksekusi frame-nya, itu akan mengambil perintah baru dari antrian perintah ketika nomor siklus perintah tercapai.

Sistem seperti itu akan bekerja dan menjadi "benar", tetapi akan sangat meningkatkan kompleksitas kode.

Pada akhirnya, saya memutuskan untuk menggunakan solusi yang lebih sederhana tanpa antrian. Karena papan utama hanya mencatat satu perintah per frame, saya menjalankan eksekusi secara bergantian pada dua komputer sehingga masing-masing melakukan dua irisan waktu per frame:

  • lakukan paruh pertama frame pada papan utama
  • lakukan paruh pertama frame pada kartu suara
  • lakukan paruh kedua frame pada papan utama
  • lakukan paruh kedua frame pada kartu suara

Ini memastikan bahwa kartu suara dengan benar melihat setiap perintah yang direkam oleh papan utama, dan pada saat yang sama dapat menjalankan setiap emulasi untuk ribuan siklus.

Tentu saja, fakta bahwa sistem host beroperasi pada frame rate 60 Hz adalah asumsi yang sangat berani :)

Dan yang terakhir ...


Fakta menarik terakhir tentang versi emulator di WebAssembly:

Ukuran terkompresi dari semua file yang diunduh saat menjalankan emulator di WebAssembly
kira-kira sama dengan 113 Kbytes:

  • sekitar 2,5 Kbytes untuk HTML, CSS, dan "tulisan tangan" JS
  • 26,8 kb per file JS runtime emscripten
  • 83,7 KB per file .wasm

File WASM berisi ROM built-in dari mesin arcade.

Terkompresi, ROM ini menempati 112 Kbytes.

Artinya, seluruh emulator terkompresi dengan ROM terintegrasi menempati volume yang hampir sama dengan ROM yang tidak terkompresi :)

ROM 112-kilobyte dikompresi hingga sekitar 57 KB, yaitu ukuran sebenarnya dari kode terkompresi dalam WASM tanpa data ROM kurang dari 30 KB (84-57).

Menurut saya cukup bagus untuk emulator penuh sistem 8-bit;)

Source: https://habr.com/ru/post/id426291/


All Articles