Wir spielen Musik von Mario ĂŒber den Systemlautsprecher

Mario  Anmerkungen

Vorwort


Hallo Welt!

Bereits seit ungefĂ€hr 3 Jahren möchte ich etwas ĂŒber Habr schreiben, aber es gab kein Thema, zu dem ich einen Beitrag posten könnte. Das war, bis ich ein wenig ĂŒber die Arbeit des System-Timers und des System-Lautsprechers fĂŒr Laborarbeiten lernen musste. Nachdem ich ein bisschen im Internet gesucht hatte, fand ich nichts Praktisches: Etwas wurde in einer zu komplizierten Sprache geschrieben, etwas war nicht besonders aussagekrĂ€ftig. Ich habe die ganze Nacht ein gutes Buch bekommen und versucht, das bekannte Thema aus dem Mario-Spiel zu spielen. Fortsetzung direkt unter dem Schnitt, es scheint, als hĂ€tten Sie es hier.

Haftungsausschluss


Der Code wird so geschrieben, wie er geschrieben ist. Der Autor ist kein Programmiergenie, sondern nur ein Student, aber er hat trotzdem versucht, den lesbarsten und verstÀndlichsten Code zu schreiben. Alles wurde in Borland C geschrieben und nur in DOSBox getestet, weil es keine etablierten Dos gibt und ich nicht wirklich mit einer Echtzeituhr versauen möchte.

ZurĂŒck im Jahr 2012Loiqig schrieb bereits eine coolere Version , schenkte der Theorie aber wenig Beachtung.

Außerdem hat der Autor (d. H. Ich) 4 Jahre musikalische Ausbildung und war arm an Solfeggio (Notenschrift).

Ein bisschen Theorie


Vor langer Zeit, als der Intel 8086-Prozessor beliebt war und der IBM-PC keine Fragen aufwirft, wurde Intel 8253 in denselben IBM-PCs und kompatiblen Computern verwendet - einem Timer und einem IntervallzĂ€hler. In modernen Computern befasst sich die SĂŒdbrĂŒcke damit ( Quelle: Wikipedia ).

Beispiel fĂŒr eine Intel 8253 PIT-Logik:

Logikdiagramm von Intel 8253. Abb.  1

Logikdiagramm von Intel 8253. Abb.  2

Wie Sie im obigen Bild sehen können, ist der Timer an die IRQ0-Leitung angeschlossen. Es wird 18,2 Mal pro Sekunde ein 8-Stunden-Interrupt generiert.

Der Timer besteht aus 3 ZÀhlern (COUNTER0-2), die unabhÀngig voneinander arbeiten.

Egal wie seltsam es auch sein mag, jeder ZĂ€hler macht seine Arbeit. In modernen Computern zĂ€hlt der erste Kanal die Tageszeit. Über den zweiten Kanal wird der DRAM neu generiert. Mit dem dritten Kanal können Sie einen Pseudozufallszahlengenerator erstellen und ihn mit einem Systemlautsprecher rasieren.

Jeder Kanal hat 6 Betriebsarten:

  • Modus 0 - Unterbrechung des Terminalkontos
  • Modus 1 - Programmierbarer Standby-Multivibrator
  • Modus 2 - Pulsfrequenzgenerator
  • Modus 3 - MĂ€andergenerator
  • Modus 4 - Software-generierter Blitz
  • Modus 5 - Hardware-basierter Blitz

Kommen wir zur Sache


Also haben wir eine kleine Theorie ĂŒber den Systemtimer gelernt und gelernt, dass der dritte Kanal mit dem Systemlautsprecher verbunden ist. Alles scheint cool zu sein. Wie kann man damit ein Thema von Mario spielen? Es ist noch nicht klar.

Jeder ZĂ€hler (Kanal) wird separat programmiert. Wir haben bereits entschieden, dass Sie den dritten verwenden mĂŒssen. Wie Sie im obigen Bild sehen können, ist der Lautsprecher an den OUT-Ausgang angeschlossen. Gleichzeitig wird es an Port 61h angeschlossen, ĂŒber den wir den Lautsprecher steuern können. Das erste (niedrigstwertige) Bit ist mit dem Gate2-Eingang verbunden und bestimmt, ob der ZĂ€hler funktioniert oder nicht. Das zweite Bit startet den Lautsprecher.

Basierend auf dieser Theorie wird die Front der Arbeit zum Spielen von Ton klar:

  • Wir programmieren CLOCK2 mit der Frequenz, die wir brauchen (dazu spĂ€ter mehr)
  • Verwenden Sie die ersten beiden Bits von 61h, um den Lautsprecher einzuschalten

Da der Standard-Systemlautsprecher nur einstimmige Sounds wiedergeben kann (genau wie bei den alten Drucktasten-Telefonen, auf denen sie den Boomer spielten), mĂŒssen wir die Frequenz jeder Note ermitteln.

Tabelle der NotenverhÀltnisse und Frequenzen


, / , / 2.

Um die gewĂŒnschte Frequenz fĂŒr unseren ZĂ€hler einzustellen, mĂŒssen Sie eine bestimmte Formel verwenden: 1193182 / N Hz, wobei 1193182 die Timerfrequenz ist (1,193182 MHz, wenn korrekt), N die Frequenz der Note, die Sie an den Lautsprecher ausgeben möchten.

//   
// _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); //     
}

Die Hauptfunktion furchtbar einfach und, offen gesagt, schlecht optimiert, aber es nicht das Wesen der Sache Àndern.

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;
}

Was ist also mit unserem Mario?


Wir haben gelernt, Sounds durch Systemdynamik zu spielen. Fein! Aber wie spielen wir eine Melodie von Mario?

Mit der Magie des Googelns finden wir die Notizen:

Notenklingeltöne von Mario
.

Als nĂ€chstes mĂŒssen Sie sich an den Verlauf der Notenschrift erinnern und jede Note aufschreiben:

Die schriftlichen Notizen und ihre Dimension:
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

Es ist erwĂ€hnenswert, dass ich mehrere Annahmen eingefĂŒhrt habe. In einer echten Melodie werden zwei Noten gleichzeitig gespielt. Ich wollte die beiden Dosboxen nicht synchronisieren, also spielte ich auf derselben Note.

Mit noch grĂ¶ĂŸerer Geduld ĂŒbersetzen wir jede Note in Frequenzen und sammeln Arrays von Frequenzen und Dauern:

// 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};

In dieser Melodie habe ich 43 Noten gezÀhlt. Es ist auch erwÀhnenswert, dass ich die Verzögerung zwischen zwei benachbarten Noten nach Gehör gewÀhlt habe. Es stellte sich etwas langsamer heraus als im Original.

Fazit


Abschließend möchte ich sagen, dass sich die Arbeit mit Hardware manchmal als interessanter herausstellt als das Schreiben einer Reihe von Code auf hoher Ebene.

Wenn jemand plötzlich meine Melodie verbessern oder etwas Eigenes schreiben möchte, können Sie dies gerne kommentieren.

PS


Wenn Sie sich entscheiden, mit der Systemdynamik zu spielen, und die Grundlagen der Notenschrift nicht kennen, finden Sie unter dem Spoiler einige Hinweise.

Musikalische Hinweise
. .
.

All Articles