Ist es möglich, im Binärformat zu programmieren?
Ist es möglich, im Binärformat zu programmieren?
Aber klar doch! Wie denkst du denn, sind die ersten Assembler entstanden?
Das hier:
Quelle: File:Pdp-11-40.jpg - Wikimedia Commons
, Stefan_Kögl [CC BY-SA 3.0 (Creative Commons - Attribution-ShareAlike 3.0 Unported - CC BY-SA 3.0
)]
ist eine PDP11; ein recht weit verbreiteter Minicomputer (ist kleiner als ein Zimmer!) in den 70ern. Und das hier:
Quelle: Retro-Computing Society of Rhode Island [CC BY-SA 3.0 (Creative Commons - Attribution-ShareAlike 3.0 Unported - CC BY-SA 3.0
)]
Ist ein besserer Blick auf das Frontpanel. Die Reihe unten sind jede Menge Schalter, mit denen man entweder eine Adresse oder ein Datenwort eingeben konnte und dann entweder in das Adressregister oder in den Speicher an der angegebenen Adresse übernehmen konnte.
Nach jedem Neustart hat man den Zettel mit dem Startprogramm aus dem Schreibtisch daneben genommen und mit Hilfe dieser Schalter das Startprogramm (zum Einlesen des Betriebssystems vom Band) eingegeben und dann den Start-Knopf gedrückt.
Was braucht man jetzt zum Programmieren?
Zunächst mal eine Tabelle die diese hier für den 6502 Prozessor:
Quelle: MOS Technology 6502 - Wikipedia
(runterscrollen bis “Assembly language instructions” und dann “unhide” :-)
Auf dieser Tabelle kann man den Binärcode aller Befehle des 6502 ablesen. Dazu sucht man erst den Befehl (samt Adressmode) in der Tabelle und liest dann am Rand die oberen 4 Bits (High nibble) und die unteren 4 Bits (Low Nibble) ab.
Ich hab den 6502 deshalb hier gewählt, weil sein Befehlssatz übersichtlich genug ist, um auf eine Seite zu passen :-) Bei einem modernen 64 bit Prozessor wäre die Tabelle wesentlich größer - aber das Prinzip bleibt das Gleiche.
Dann muss man noch über alle Eigenheiten des verwendeten Prozessors Bescheid wissen. Beim 6502 sind das z.B.:
Alle Datenregister sind 8bit breit.
Der Prozessor hat 16bit Adressen (64 kByte!) und das Adressformat ist “little Endian” (Bei Multibyte Adressen zuert das niedrige Byte, dann das Hohe).
Das Ziel aller arithmetischen und logischen Operationen ist immer der Accumulator (Register A).
Die ersten 256 Adressen im Speicher sind privilegiert, weil diese sich mit einer 8bit Adresse anstatt den vollen 16bit ansprechen lassen. Das geht schneller (der Prozessor muss weniger Code einlesen) und braucht weniger Programm.
Es gibt nur die Möglichkeit einer Addition inklusive Carry bit. (Andere Prozessoren hatten 2 Additionsbefehle mit und ohne Carry)
Für’s erste reicht das mal, wenn ich alle die lustigen Dinge die man mit dem X- und Y-Register anstellen kann, dann säßen wir morgen immer noch hier :-)
Also, jetzt lass uns mal ein Programm schreiben: (Zahlen die mit $ beginnen sind hex; “;” beginnt Kommentare (bis Ende Zeile)
Wir wollen 3 Daten addieren: eine auf Adresse $1400, eine auf Adresse $0080 und dazu noch den fixen Wert 42. Also lass uns loslegen:
; Lade die Zahl aus Adresse $1400 in den Accumulator. 
; Dazu suchen wir in der Tabelle nach "LDA abs" 
; (LDA = Lade den Accumulator, abs = absolute (16bit Adresse) 
; Nach ein wenig starren finden wir das in der Tabelle auf Position 
; High Nibble = A und Low Nibble = D 
; jetzt können wir den Befehl in Hex hinschreiben (Little Endian nicht vergessen): 
AD 00 14 
; Und in binär: 
1010 1101 0000 0000 0001 0100 
 ; Weiter zum nächsten Befehl: Addiere den Wert aus Speicheradresse $0080 
; Zunächst fällt auf, dass die Speicheradresse im der Zero-Page liegt; 
; wir können also die Kurzform nehmen. 
; Aber halt, die Addition geht ausschließlich MIT Carry; das ist aber 
; blöd, weil wir nicht wissen ob das Flag gesetzt ist oder nicht. 
; Also schauen wir mal nach, ob es eine Möglichkeit gibt, dass Carry-Bit 
; gezielt zu setzen. Und wir finden CLC (Clear Carry) auf: 
18 
; binär: 
0001 1000 
; Und jetzt suchen wir nach ADC zpg (Add with Carry, Zero-Page) 
; und finden den Befehl auf 6 5. Damit lauten der nächste Befehl in Hex: 
65 80 
; und in binär: 
0110 0101 1000 0000 
; Jetzt wird es etwas kompliziert: das maximale Ergebnis was wir mit 
; 2 Byte add + 42 (= $2A) erzielen könnten wäre: 
; $2A + $FF + $FF = $22A 
; das ist blöd, weil das einzelne Carry nicht mehr reicht; wir müssen 
; also was tun. Ich beschließe hier: ignorieren! 
; also suche wir als nächstes in der Tabelle nach: ADC # (Add with Carry 
; und # für Wert kommt im Befehl) und finden das auf: 6 9. Dass 42 
; dezimal = $2A ist haben wir ja schon 
; Also in Hex: 
69 2A 
; und in binär: 
0110 1001 0010 1010 
Jetzt müssen wir das nur noch für die paar hundert bis tausend weiterer Befehle für unser Programm so weiter machen und können dann am Schluss unser Programm binär hinschreiben:
1010 1101 0000 0000 0001 0100 ; LDA $1400 
0001 1000   ; CLC 
0110 0101 1000 0000  ; ADC $80 
0110 1001 0010 1010  ; ADC #$2A 
Jetzt stellst du dir vor, der ALTAIR 8800 hätte anstatt des 8080 den 6502 gehabt und du könntest jetzt hier:
Quelle: Todd Dailey [CC BY-SA 2.0 (Creative Commons - Attribution-ShareAlike 2.0 Generic - CC BY-SA 2.0
)] via WikiCommons: File:Altair 8800 at the Computer History Museum, cropped.jpg
jetzt dein Programm eingeben.
Du kannst dir gar nicht vorstellen, wie schnell der erste Assembler erfunden wurde :-)