Zur Haupt­na­vi­ga­ti­on sprin­gen [Alt]+[0] Zum Sei­ten­in­halt sprin­gen [Alt]+[1]

Pro­gram­mie­rung: Hoch­spra­che, As­sem­bler und Ma­schi­nen­spra­che

Da sich die Si­mu­la­ti­on der Re­gis­ter­ma­schi­ne am von-Neu­mann-Rech­ner ori­en­tiert, lie­gen Be­feh­le und Daten im Ar­beits­spei­cher. Sie sind also binär co­diert, also als Folge von Nul­len oder Ein­sen ab­ge­legt. Diese Dar­stel­lung ist für den Men­schen nicht les­bar, die Ma­schi­ne kann diese Zei­chen­fol­gen aber in­ter­pre­tie­ren. Diese In­ter­pre­ta­ti­on ba­siert auf der so­ge­nann­ten Ma­schi­nen­spra­che.

Die Ma­schi­nen­spra­che be­zeich­net ein Sys­tem von In­struk­tio­nen, die der je­wei­li­ge Pro­zes­sor di­rekt aus­füh­ren kann. Ver­ein­facht ge­sagt sind der Um­fang und die Aus­prä­gung des In­struk­ti­ons­sat­zes von den im Pro­zes­sor um­ge­setz­ten Schalt­net­zen ab­hän­gig. Jede zur Ver­fü­gung ste­hen­de In­struk­ti­on ist durch eine fest­ge­leg­te Bit­fol­ge von Nul­len und Ein­sen an­sprech­bar. Diese legt den Ein­sprungs­punkt im Mi­kro­pro­gramm­spei­cher fest.

Zum bes­se­ren Um­gang mit der Ma­schi­nen­spra­che wer­den die zu­läs­si­gen Bit­fol­gen, die Ma­schi­nen­be­feh­le, Adres­sen usw. mit sym­bo­li­schen, für den Men­schen ver­ständ­li­chen Namen be­legt. Diese Be­feh­le wer­den As­sem­bler­be­feh­le ge­nannt; sie er­mög­li­chen die Im­ple­men­tie­rung von As­sem­bler­pro­gram­men.

Mit einem spe­zi­el­len pro­zes­sor­spe­zi­fi­schen Werk­zeug, dem As­sem­bler, kann ein As­sem­bler­pro­gramm dann meist na­he­zu eins zu eins in ein ent­spre­chen­des Ma­schi­nen­pro­gramm um­ge­wan­delt wer­den.

Bei­spiel:

As­sem­bler­be­fehl Ma­schi­nen­code
Ope­ra­ti­on Ope­rand
MOV AX,[20] 00010000 00100000
SUB AX,[21] 00110100 00100001
JS 12 01100101 00010010
DEC AX 01011110 00000000

In As­sem­bler oder gar Ma­schi­nen­spra­che pro­gram­miert heute fast nie­mand mehr, da diese Pro­gram­me sehr un­über­sicht­lich und sehr schlecht auf an­de­ren Com­pu­ter­ty­pen über­trag­bar sind. Le­dig­lich bei sehr zeit­kri­ti­schen An­wen­dun­gen kann dies noch sinn­voll sein. Daher ver­wen­den wir Hoch­spra­chen, die rech­ner­un­ab­hän­gi­ger sind, we­sent­lich kom­ple­xe­re Be­feh­le und Mög­lich­kei­ten zur Struk­tu­rie­rung des Pro­gramm­codes be­sit­zen (z.B. Ob­jekt­ori­en­tier­te Pro­gram­mie­rung).

Um das Pro­gramm auf einem Com­pu­ter aus­füh­ren zu kön­nen, muss es aber in Ma­schi­nen­spra­che über­setzt wer­den. Diese Auf­ga­be haben Com­pi­ler und In­ter­pre­ter. Ein Com­pi­ler über­setzt das Pro­gramm, so­bald der Quell­text fer­tig­ge­stellt ist. Da­nach liegt ein aus­führ­ba­res Pro­gramm (bei Win­dows: z.B. exe-File) vor, das Ma­schi­nen­be­feh­le ent­hält. Ein In­ter­pre­ter über­setzt das Pro­gramm erst dann, wenn es be­nö­tigt wird. Im Ex­trem­fall wird jeder ein­zel­ne Be­fehl erst über­setzt und dann aus­ge­führt. Dies ver­lang­samt die Aus­füh­rung er­heb­lich.

Spra­chen mit Com­pi­ler sind bei­spiels­wei­se Del­phi und C++. Be­kann­te In­ter­pre­ter­spra­chen sind Basic oder PHP. Java stellt eine Zwi­schen­lö­sung dar. Java-Pro­gram­me wer­den von einem Com­pi­ler in Java-Byte-Code über­setzt (class- bzw. jar-File). Die­ser wird dann bei der Aus­füh­rung von einem In­ter­pre­ter (Java Vir­tu­al Ma­chi­ne = Java Run­ti­me En­vi­ro­ment JRE) in Ma­schi­nen­be­feh­le über­setzt und aus­ge­führt. Da Java-Pro­gram­me erst zur Lauf­zeit in Ma­schi­nen­be­feh­le über­setzt wer­den, sind sie im Prin­zip auf jedem Rech­ner lauf­fä­hig für den es die JRE gibt. Dafür sind sie aber lang­sa­mer als exe-Files.

Da die Re­gis­ter­ma­schi­ne in Bezug auf die Be­re­chen­bar­keit ge­nau­so mäch­tig ist, wie ein rea­ler Com­pu­ter, müs­sen sich Pro­gram­me in einer Hoch­spra­che in Pro­gram­me für eine Re­gis­ter­ma­schi­ne über­set­zen las­sen. Für einen kon­kre­ten Si­mu­la­tor ist das auf­grund von Be­schrän­kung des Spei­cher­plat­zes und der Re­gis­ter­brei­te zu­min­dest ein­ge­schränkt mög­lich.

Va­ria­blen:

Va­ria­blen wer­den im RAM ge­spei­chert. Bei der De­kla­ra­ti­on der Va­ria­ble muss fest­ge­legt wer­den, an wel­cher Stel­le im RAM sie ge­spei­chert wird. Jeder Zu­griff auf eine Va­ria­ble wird dann in eine As­sem­bler­an­wei­sung mit Adress­an­ga­be (z.B. MOV AX, [30], wenn die Va­ria­ble an RAM-Stel­le 30 steht) über­setzt. Ar­rays las­sen sich da­durch rea­li­sie­ren, dass die Adres­se des ers­ten Ar­ray­ele­ments in ein Re­gis­ter (z.B. BX) über­tra­gen, der Index des be­nö­tig­ten Ele­ments dazu ad­diert und dann ein Be­fehl wie MOV AX,[BX] ver­wen­det wird.

Zu­wei­sun­gen:

Um Va­ria­blen neue Werte zu­zu­wei­sen, müs­sen die neuen Werte zu­nächst in einem Re­gis­ter (meist AX) vor­lie­gen. Dort wer­den ge­ge­be­nen­falls auch Be­rech­nun­gen durch­ge­führt. Da­nach wird das Er­geb­nis an die Spei­cher­stel­le der Va­ria­blen im Ar­beits­spei­cher über­tra­gen.

Sol­len kom­ple­xe Terme be­rech­net wer­den, ist es sinn­voll mit einem Stack zu ar­bei­ten. Ein Stack ist ein Spei­cher­be­reich, für den es im We­sent­li­chen zwei Ope­ra­tio­nen gibt: PUSH legt eine neue Zahl auf den Sta­pel, POP holt die obers­te Zahl vom Sta­pel und ent­fernt sie dort. Wan­delt man einen Term in die Post­fix-No­ta­ti­on um, lässt er sich gut mit einem Sta­pel be­rech­nen. Die Be­rech­nung er­folgt streng von links nach rechts, da man ohne Klam­mern aus­kommt und auch Punkt vor Strich schon be­rück­sich­tigt ist.

z.B. aus 3 * 5 + (3 – 2) wird 3 5 * 3 2 - +

Die Ope­ran­den wer­den zu­nächst auf den Sta­pel ge­legt und immer wenn eine Re­chen­ope­ra­ti­on durch­ge­führt wer­den soll, wer­den die obers­ten bei­den Zah­len dem Sta­pel ent­nom­men, damit die Be­rech­nung durch­ge­führt und das Er­geb­nis wie­der auf dem Sta­pel ab­ge­legt.

z.B. 3 und 5 wer­den auf den Sta­pel ge­legt, dann die Mul­ti­pli­ka­ti­on aus­ge­führt und 15 wie­der auf den Sta­pel ge­legt. Dazu kom­men 3 und 2 auf den Sta­pel. Da­nach wird die Sub­trak­ti­on mit 3 und 2 aus­ge­führt und das Er­geb­nis 1 auf den Sta­pel ge­legt. Am Ende wer­den die 15 und die 1 vom Sta­pel ent­nom­men, ad­diert und das Er­geb­nis 16 auf den Sta­pel ge­legt.

Ent­schei­dun­gen:

Ablauf

Der Ver­gleich zwei­er Va­ria­blen/Werte wird durch eine Sub­trak­ti­on rea­li­siert. Je nach­dem, ob das Er­geb­nis po­si­tiv, ne­ga­tiv oder gleich Null ist, kann ent­schie­den wer­den, wel­che Zahl grö­ßer war. Durch einen be­ding­ten Sprung wird dann ggf. der IF-Teil über­sprun­gen. Falls es einen ELSE-Teil gibt, muss die­ser nach der Aus­füh­rung des IF-Teils über­sprun­gen wer­den.

Be­din­gun­gen mit „grö­ßer“ oder „klei­ner“ wer­den dabei mit einem JS, bzw. JNS rea­li­siert, Be­din­gun­gen mit „gleich“ durch ein JZ oder JNZ. „grö­ßer-gleich“ bzw. „klei­ner-gleich“ wer­den in „nicht klei­ner“ oder „nicht grö­ßer“ um­ge­wan­delt.

Schlei­fen:

Ablauf

While-Schlei­fen las­sen sich ähn­lich wie Ent­schei­dun­gen rea­li­sie­ren. Zu­nächst wird die While-Be­din­gung über­prüft. Ist sie nicht er­füllt, er­folgt ein (be­ding­ter) Sprung hin­ter die Schlei­fe. An­dern­falls wer­den die An­wei­sun­gen, die wie­der­holt wer­den sol­len, aus­ge­führt. Da­nach er­folgt ein un­be­ding­ter Sprung zur er­neu­ten Über­prü­fung der Be­din­gun­gen.

For-Schlei­fen las­sen sich in While-Schlei­fen um­schrei­ben. Dazu muss der In­itia­li­sie­rungs­teil als An­wei­sung vor der Schlei­fe aus­ge­führt wer­den. Die Er­hö­hung der Zähl­va­ria­blen wird vor dem un­be­ding­ten Sprung durch­ge­führt.

Damit ist der Weg vom Mi­kro­pro­zes­sor zur Hoch­spra­che, bzw. um­ge­kehrt voll­stän­dig. Ein Com­pu­ter­pro­gramm einer Hoch­spra­che wird von einem au­to­ma­ti­schen Com­pi­ler in Ma­schi­nen­code über­setzt. Die­ser wird von einem Com­pu­ter mit von-Neu­mann Ar­chi­tek­tur ver­ar­bei­tet, indem Be­fehl für Be­fehl aus dem Ar­beits­spei­cher ge­la­den und mit Hilfe des Mi­kro­pro­gramm­spei­chers in Steu­er­si­gna­le für den Pro­zes­sor über­setzt wird. Der Pro­zes­sor be­steht aus elek­tro­ni­schen Schalt­net­zen. Durch Ak­ti­vie­rung der Steu­er­si­gna­le wird das rich­ti­ge Schalt­netz aus­ge­wählt und die Be­rech­nung durch­ge­führt. Die Schalt­net­ze sind aus ein­fa­chen lo­gi­schen Bau­ele­men­ten auf­ge­baut, die ih­rer­seits aus we­ni­gen CMOS-Feld­ef­fekt­tran­sis­to­ren be­ste­hen. Ein Pro­gramm in einer Hoch­spra­che wird im End­ef­fekt durch elek­tri­sche Si­gna­le in einer Viel­zahl von Tran­sis­to­ren aus­ge­führt.

 

 

Hin­ter­grund­in­for­ma­tio­nen: Her­un­ter­la­den [odt][4 MB]

 

Wei­ter zu Li­te­ra­tur­hin­wei­se