Home

MASM32-
Infoseite

HD.exe: hexadezimale
Dateianzeige

Ein Icon und mehr für das ausführbare
Programm mittels Ressource-Datei

MASM32 SDK (Windows Konsole):
HD.exe: kleine Optimierungen

Hier geht es um einige Optimierungen am Programm der vorigen Webseite.
Stringbefehl und Ausrichten auf Doppelwortgrenze

Beim Merkmal ha00 steht diese Befehlsfolge:

    	cld                      ;richtung aufwaerts
ha00:   lea     edi,dz           ;"druckzeile"
	mov	ecx,llz
@@:     mov     al,[esi]
        mov     [edi],al
        inc     esi
        inc     edi
        loop    @B
Zwischen @@ und dem loop @B wird jedes Byte der dz Druckzeile durch ein korrespondierendes Byte aus der Leerzeile lz überschrieben. Dazu werden 5 Befehle verwendet und die Druckzeile ist 81 Bytes lang: Es werden 405 Befehle ausgeführt.

Zur Ausführung genau dieser Aufgabe gibt es jedoch spezielle Befehle in der Klasse der Stringbefehle. Es passt der Stringbefehl movsb mit vorgestelltem Wiederholungspräfix rep.

Damit es in der richtigen Reihenfolge von links nach rechts abläuft, muss das Richtungsflag passend gesetzt sein. Das wird durch den Befehl cld erreicht.

So sieht der Programmteil nach der Änderung aus:

 
        cld                      ;richtung aufwaerts
        push ds                  ;ermoeglich die Verwendung von ret movsb 
        pop  es   
 ha00:  lea     edi,dz           ;"druckzeile"
	mov	ecx,llz
        rep     movsb
In diesem Fall wird movsb 81-mal ausgeführt. Das sind 324 Befehlsausführungen weniger als in der vorhergehenden Version.

Allerdings greifen die modernen Prozessoren nicht auf einzelne Bytes aus dem Hauptspeicher zu. Sie schnappen sich vier Bytes auf einmal. Wenn man dies ausnutzt, könnte man mit einem Viertel der Befehlsausführungen auskommen. Allerdings muss man aufrunden, denn 81/4 ergibt einen Rest.

Glücklicherweise kann das Assemblierprogramm rechnen. (81 + 3)/4 ergibt die gewünschte Anzahl Wiederholungen. Das zugeordnete Symbol ist im Bereich .data definiert und heißt llz4:

        llz     equ     $ - lz
        llz4	equ	(llz+3)/4
 
Wenn nun statt movsb das passende movsd verwendet wird, kommen wird im Prinzip mit 21 Befehlsausführungen aus. Allerdings funktioniert anschließend das Programm nicht mehr, denn die letzte Übertragung überschreibt den Inhalt des Arbeitsfeldes dzpos (vergleiche Programmliste auf der vorhergehenden Seite). Man müsste zusätzlich drei „Schutzbytes” hinter der Druckzeilendefinition im Datenbereich definieren. Das lassen wir jedoch sein, denn es gibt eine bessere Möglichkeit!
 
        cld                      ;richtung aufwaerts
        push ds                  ;ermoeglich die Verwendung von ret movsd 
        pop  es   
 ha00:  lea     edi,dz           ;"druckzeile"
	mov	ecx,llz4
        rep     movsd
 
Die bessere Möglichkeit ist die Verwendung der Direktive ALIGN. Sie wurde geschaffen, weil man sich sonst nicht aussuchen kann, mit welcher zusammenhängenden Gruppe von 4 zusammenhängenden Bytes sich der movsd beschäftigt. Wenn die vier zusammenhängenden Bytes auf einer durch 4 ohne Rest teilbaren Speicheradresse stehen, reicht ein Speicherzugriff aus. Tun sie es nicht, führt movsd sind zwei aufeinanderfolgende Zugriffe aus. Dies gilt für das Lesen und auch für das Schreiben.

ALIGN 4 fügt für die Ausrichtung passende Schutzbytes hinzu. Die folgende Tabelle zeigt es.

Adresse
oder Wert
Name Befehl Wert, ggf. Kommentar
000000B7 cnt db 0 ;zeilenzaehler
    ALIGN 4 ;Ausrichten auf Doppelwortgrenze
000000B8 lz db '........: ',50 dup (spc),pipe,spc,16 dup ('.'),cr,lf,0
= 00000051 llz equ $ - lz
= 00000015 llz4 equ (llz+3)/4
ALIGN 4 ;Ausrichten auf Doppelwortgrenze
0000010C dz db llz-19 dup (spc)

Bei dem ersten ALIGN 4 ist keine Ausrichtung erforderlich, denn das nächste Feld lz beginnt auch ohne zusätzliche Ausrichtung auf einer durch 4 ohne Rest dividierbaren Speicheradresse: 0b8h entspricht dezimal 168.

Das ab 0B8h definierte Textfeld lz ist 51h Bytes lang. Ohne das ALIGN 4 wurde das nachfolgende Textfeld dz ab Adresse 109h beginnen:

0b8h + 51h = 109h

109h (entsprcht 265 dezimal) und ist somit nicht ohne Rest durch 4 teilbar. In diesem Fall wird urch das ALIGN 4 auf die nächste ohne Rest durch 4 teilbare Speicheradresse vorgerückt.

Es ist die Speicherdaresse 10Ch, entsprechend 268 dezimal.

Zwei Tabellenzeilen beginnen mit einem Gleichheitszeichen. Sie zeigen die Werte, die die beiden Symbole llz und llz4 erhalten. Die Symbole geben die Anzahl Wiederholungen der Übertragungen an.

Der alte Schiebebefehl
Teile des Programmes stammen noch aus der Zeit der 8086. Seitdem sind einige Assemblerbefehle hinzugekommen und einige alte Assemblerbefehle wurden um zusätzlich Möglichkeiten erweitert. Dazu gehört der Befehl shr. Beim 8086 konnte man ohne Vorbereitung nur um 1 Bit rotieren. Deshalb steht im Programmtext der Befehl 4-mal untereinander. Ab dem 80186 kann man mit einem shr bis zu 31 Schiebetakte ausführen. Die vier Schiebebefehle lassen sich somit durch einen einzigen shr  al,4 ersetzen.

Schaut man sich den im Quelltext folgenden Programmteil an, stellt man fest: Dass Register ah wird nicht verändert. Somit kann man die Sicherung des Registerpaares ax im Stack durch die schneller ausgeführte Sicherung des Registers al in ah ersetzen. Die Tabelle zeigt den betreffenden Programmabschnitt vor und nach der Optimierung:

byte_hex:
        push    ax
        shr     al,1
        shr     al,1
        shr     al,1
        shr     al,1
        call    byte_hex1
        pop     ax
byte_hex:
        mov     ah,al
        shr     al,4
        call    byte_hex1
        mov     al,ah
Auch in dem Unterprogramm pa00 gibt es Verbesserungsmöglichkeiten. Die vorsorgliche Sicherung des Registers ecx kann ersatzlos entfallen. Beim Register ebx ist sie erforderlich, denn im Unterprogramm byte_hex wird ebx verändert. Wenn man jedoch edx verwendet, dann spart man die Stacksicherung von ebx ein.
;Unterprogramm zur 4-stelligen Adressausgabe
;(8 Hexadezimalziffern)
pa00: lea  ebx,dz    ;Anfangsadresse Druckzeile
      mov  dzpos,ebx
      mov  ebx,offset address + 3
      mov  ecx,4     ;Adresse vierstellig hexa
@@:   push ecx       ;ausgeben (rueckwaerts)
      mov  al,[ebx]
      dec  ebx
      push ebx       ;Upro byte_hex zerstoert ebx
      call byte_hex
      pop  ebx
      pop  ecx
      loop @B
      ret
;Unterprogramm zur 4-stelligen Adressausgabe
;(8 Hexadezimalziffern)
pa00: lea  edx,dz     ;Anfangsadresse Druckzeile
      mov  dzpos,ebx
      mov  edx,offset address + 3
      mov  ecx,4      ;Adresse vierstellig hexa
@@:                   ;ausgeben (rueckwaerts)
      mov  al,[ebx]
      dec  edx
      call byte_hex
      loop @B
      ret

Letztes Upload: 30.03.2017 um 14:31:29