Kami memutar musik dari Mario di speaker sistem

Mario  Catatan

Kata pengantar


Halo Dunia!

Sudah sekitar 3 tahun saya ingin menulis sesuatu tentang Habr, tetapi tidak ada topik untuk memposting tulisan. Itu sampai saya perlu belajar sedikit tentang kerja pengatur waktu sistem dan pengeras suara sistem untuk pekerjaan laboratorium. Setelah sedikit menjelajahi internet, saya tidak menemukan sesuatu yang praktis: sesuatu ditulis dalam bahasa yang terlalu rumit, sesuatu yang tidak terlalu berarti. Saya mendapat buku yang bagus, sepanjang malam dan mencoba memainkan tema terkenal dari game Mario. Kelanjutan tepat di bawah luka, sepertinya Anda memilikinya di sini.

Penolakan


Kode ini ditulis sebagaimana tertulis. Penulis bukan seorang jenius pemrograman, tetapi hanya seorang siswa, tetapi bagaimanapun, ia mencoba untuk menulis kode yang paling mudah dibaca dan dimengerti. Semuanya ditulis dalam Borland C dan diuji dalam DOSBox hanya karena tidak ada dos yang ditetapkan dan saya tidak benar-benar ingin mengacaukan dengan jam waktu nyata.

Kembali pada tahun 2012Loiqig sudah menulis versi yang lebih keren , tapi, bagiku, sedikit memperhatikan teori.

Juga, penulis (yaitu, saya) memiliki 4 tahun pendidikan musik dan miskin dalam solfeggio (notasi musik).

Sedikit teori


Dahulu kala, ketika prosesor Intel 8086 populer, dan PC IBM tidak mengajukan pertanyaan, Intel 8253 digunakan dalam PC IBM yang sama dan komputer yang kompatibel - timer dan penghitung interval. Di komputer modern, jembatan selatan berurusan dengan ini ( Sumber: Wikipedia ).

Contoh logika PIT Intel 8253:

Diagram logika Intel 8253. Gbr.  1

Diagram logika Intel 8253. Gbr.  2

Seperti yang Anda lihat pada gambar di atas, timer terhubung ke jalur IRQ0. Ini menghasilkan interupsi 8 jam 18,2 kali per detik.

Timer terdiri dari 3 penghitung (COUNTER0-2), yang beroperasi secara independen satu sama lain.

Tidak peduli betapa anehnya itu, setiap konter melakukan tugasnya. Di komputer modern, saluran pertama menghitung waktu dalam sehari. Menggunakan saluran kedua, DRAM dibuat ulang. Menggunakan saluran ketiga, Anda dapat membuat generator angka pseudo-acak dan mencukurnya dengan speaker sistem.

Setiap saluran memiliki 6 mode operasi:

  • Mode 0 - gangguan akun terminal
  • Mode 1 - Multivibrator Siaga yang Dapat Diprogram
  • Mode 2 - Generator Frekuensi Denyut
  • Mode 3 - berliku-liku generator
  • Mode 4 - strobo yang dihasilkan perangkat lunak
  • Mode 5 - strobo berbasis perangkat keras

Mari kita mulai bisnis


Jadi, kami belajar sedikit teori tentang timer sistem, mengetahui bahwa saluran ketiga terhubung ke speaker sistem. Segalanya tampak keren. Bagaimana cara menggunakan ini untuk memainkan tema dari Mario? Belum jelas.

Setiap penghitung (saluran) diprogram secara terpisah. Kami telah memutuskan bahwa Anda perlu menggunakan yang ketiga. Seperti yang dapat Anda lihat pada gambar di atas, speaker terhubung ke output OUT. Pada saat yang sama, itu terhubung ke port 61h, yang dengannya kita dapat mengontrol speaker. Bit pertama (paling tidak signifikan) terhubung ke input Gate2 dan menentukan apakah penghitung bekerja atau tidak. Bit kedua memulai speaker.

Berdasarkan teori ini, bagian depan pekerjaan untuk memainkan suara menjadi jelas:

  • Kami memprogram CLOCK2 pada frekuensi yang kami butuhkan (lebih lanjut tentang itu nanti)
  • Gunakan dua bit pertama dari 61 jam untuk menghidupkan speaker

Karena speaker sistem default hanya dapat memutar suara satu-suara (yah, seperti pada telepon tombol-tekan lama, di mana boomer dimainkan), kita perlu mendapatkan frekuensi setiap not.

Daftar rasio catatan dan frekuensi


, / , / 2.

Untuk mengatur frekuensi yang diinginkan untuk penghitung kami, Anda perlu menggunakan rumus tertentu: 1193182 / N Hz, di mana 1193182 adalah frekuensi penghitung waktu (1,193182 MHz jika harus benar), N adalah frekuensi catatan yang Anda putuskan untuk output ke speaker.

//   
// _freq —  
// _dur —  
// _del —    
void play_sound(int _freq, int _dur, int _del)
{
	outp(0x43, 0xb6); //     2 ()

	int timer_soundFreq = TIMER_FREQUENCY/_freq; //    
                                                    //.
                                                   // TIMER_FREQUENCY = 1193182

	//     
	outp(0x42, timer_delay & 0x00ff); //   
	outp(0x42, (timer_delay & 0xff00) >> 8); //  


	outp(0x61, inp(0x61) | 3); //  


	delay(_dur); //  ,     ,
		    //   

	outp(0x61, inp(0x61) & 0xfc); //  

	delay(_del); //     
}

Fungsi utama sangat sederhana dan, terus terang, kurang dioptimalkan, tetapi tidak mengubah esensi masalah ini.

int main(int argc, char const *argv[])
{
	for (size_t i = 0; i < N; ++i) // N —  ,  
	{
		play_sound(FREQUENCY[i], DURATION[i], DELAY[i]);
	}
	return 0;
}

Jadi bagaimana dengan mario kita?


Kami belajar memainkan suara melalui dinamika sistem. Baik! Tapi bagaimana kita memainkan lagu dari Mario?

Menggunakan keajaiban googling kami menemukan catatan:

Lembar nada dering musik dari Mario
.

Selanjutnya, Anda harus mengingat jalan notasi musik dan menulis setiap catatan:

Catatan tertulis dan dimensi mereka:
2 — 1/4
2 — 1/4
2 — 1/8
2 — 1/8
2 — 1/4

2 — 1/4
— 1/4

2 — 1/4
— 1/4
— 1/4

— 1/4
— 1/4
() — 1/8
— 1/4

— 1/4
2 — 1/4
2 — 1/4
2 — 1/4
2 — 1/8
2 --1/8

2 — 1/4
2 — 1/8
2 — 1/8
— 1/8

2 — 1/4
()2 — 1/4
()2 — 1/8
()2 — 1/4
()2 — 1/8

() — 1/4
() — 1/4
2 — 1/4
— 1/4
2 — 1/4
2 — 1/4

2 — 1/4
()2 — 1/4
()2 — 1/8
2 — 1/4
2 — 1/4

3 — 1/4
3 — 1/8
3 — 1/4

Perlu dicatat bahwa saya memperkenalkan beberapa asumsi. Dalam melodi nyata, dua not dimainkan secara bersamaan. Saya tidak ingin menyinkronkan dua dosbox, jadi saya memainkan not yang sama.

Berbekal kesabaran yang bahkan lebih besar, kami menerjemahkan setiap not ke frekuensi dan mengumpulkan array frekuensi dan durasi:

// Mario -- 43
int FREQUENCY[] = {659.255, 659.255, 659.255, 523.251, 659.255, 783.991, 391.995, 523.251, 391.995, 329.628, 440, 493.883, 466.164, 440, 391.995, 659.255, 783.991, 880, 698.456, 783.991, 659.255, 523.251, 587.33, 987.767, 783.991, 680.255, 698.456, 622.254, 680.255, 415.305, 466.164, 523.251, 440, 523.251, 587.33, 783.991, 739.989, 729.989, 587.33, 659.255, 1046.502, 1046.502, 1046.502};

int DURATION[] = {300, 300, 160, 160, 300, 300, 300, 300, 300, 300, 300, 300, 160, 300, 300, 300, 300, 300, 160, 160, 300, 160, 160, 160, 300, 300, 160, 300, 160, 300, 300, 300, 300, 300, 300, 300, 300, 160, 300, 300, 300, 160, 300};

int DELAY[] = {35, 35, 50, 35, 35, 350, 200, 35, 35, 200, 35, 35, 35, 200, 35, 35, 35, 35, 35, 200, 35, 35, 35, 35, 35, 35, 35, 35, 200, 35, 35, 35, 35, 35, 200, 35, 35, 35, 35, 200, 35, 35, 0};

Dalam melodi ini, saya menghitung 43 not. Perlu juga dicatat di sini bahwa saya memilih penundaan antara dua not yang berdekatan. Ternyata sedikit lebih lambat daripada yang asli.

Kesimpulan


Sebagai kesimpulan, saya ingin mengatakan bahwa bekerja dengan perangkat keras kadang-kadang ternyata lebih menarik daripada menulis banyak kode tingkat tinggi.

Jika seseorang tiba-tiba ingin meningkatkan melodi saya atau menulis sesuatu sendiri, maka Anda dapat berkomentar.

PS


Jika Anda memutuskan ingin bermain di dinamika sistem dan Anda tidak tahu dasar-dasar notasi musik, maka di bawah spoiler Anda dapat menemukan beberapa petunjuk.

Petunjuk musik
. .
.

All Articles