Ms Dos 1.0 hat geschrieben:... dann gibt es noch eine möglichkeit mit inline ASM die ich im netz gefunden habe die bei mir leider nicht geht da mein Compiler bei inlineasm leider fehler wirft.
Mhm, das ist doof. Hierbei kann ich nicht helfen.
Code: Alles auswählen
// memwrite()
// schreibt ein Byte zu der RAM-Speicherzelle, die durch ihre
// Segment:Offsetadresse in adh:adl angegeben ist.
void memwrite(unsigned adh, unsigned adl, unsigned char val)
{
asm{ mov BX, adh
mov ES,BX
mov BX, adl
mov AH, val
mov ES:[BX], AH }
}
// memread()
// Liest von der Segment:Offsetadresse adh:adl ein Byte
// Rückgabe das gelesene Byte
// Hinweis:
// Es werden drei Varianten von memread() angeboten, die alle drei
// gleiches tun und zeigen wie ein Zeiger aufgebaut ist.
// Variante 1 basiert auf einer Assemblerprogrammierung
// Variante 2 basiert auf der Benutzung von MK_FP()
// Variante 3 basiert auf der Zeigerarithmetik
unsigned char memread (unsigned adh, unsigned adl, unsigned n)
{
asm{ mov BX, adh
mov ES,BX
mov BX, adl
add BX, n
mov AH, ES:[BX] }
return(_AH);
}
mfg :)
Anstelle von BX als Offset-Adressregister kann man auch SI, DI, oder wie hier bei der Verwendung eines Segment-Overide-Prefixes(ES:) auch BP als Offset-Adressregister verwenden.
Anstelle des ES-Segmentregisters kann man auch das DS-Segmentregister, oder ab 80386er über das FS- oder über das GS-Segmentregister adressieren.
Ohne Angabe eines Segment-Overide-Prefixes wird das DS-Segmentregister verwendet. (Ausnahme: BP und SP sind dem SS-Segmentregister zugeordnet.)
Beispiele für Schreibzugriffe in den Ram:
mov AX, adh ; Segmentadresse
mov DS, AX ; ins DS-Segmentregister schreiben
mov SI, adl ;Offsetadresse
mov AL, val ; Wert
mov [SI], AL ; nach DS:SI schreiben
oder
mov CX, adh ; Segmentadresse
mov FS, CX ; ins FS-Segmentregister schreiben
mov BP, adl ; Offsetadresse
mov CH, val ; Wert
mov FS:[BP], CH ; nach FS:BP schreiben
Wenn man 32Bit-Register verwendet kann man jedes Allzweckregister als Offsetregister verwenden:
mov BX, adh ; Segmentadresse
mov ES, BX ; ins ES-Segmentregister schreiben
mov EAX, adl ; Offsetadresse
mov BL, val ; Wert
mov ES:[EAX], BL ; nach ES:EAX schreiben
Offsetregister lassen sich auch kombinieren:
mov ES:[SI+DI], BL
mov ES:[EAX+ECX*4], BL ; Offset-Adresse bildet sich aus: EAX + (ECX * 4)
mov ES:[EAX+1000h], BL
So eine Addition und Multiplikation zur Adressberechnung erfolgt schneller als mit dem ADD-Befehl und/oder dem MUL-Befehl.
Für String-Operationen gibt es noch andere Befehle um auf Daten im Ram zuzugreifen. Hierfür gibt es eine Festlegung auf bestimme Register.
Zum Lesen verwendet man DS:SI mit dem lodsb/lodsw/lodsd -Befehl(für bytes, word und doppelword) und zum Schreiben verwendet man ES:DI mit dem stosb/stosw/stosd -Befehl jeweils zusammen mit dem AL/AX/EAX-Register.
Hierbei wird z.B. von der Adresse in DS:SI mit lodsb ein Byte in das AL-Register geladen und SI wird je nachdem ob das Direktion-Flag gesetzt ist entweder verringert oder wenn nicht gesetzt erhöht.
Daneben kann man auch Bytes/Word/DWord kopieren mit dem movsb/movsw/movsd -Befehl.
Beipiel zum Lesen:
cld ; clear D-Flag
mov AX, adh
mov DS, AX
mov SI, adl
lodsb
Danach befindet sich im AL-Register der Inhalt aus der Adresse in DS:SI.
Beispiel zum Schreiben:
cld
mov AX, adh
mov ES, AX
mov DI, adl
mov AL, val
stosb
Beispiel zum Kopieren:
cld
mov AX, adh1 ; Quelle
mov DS, AX
mov SI, adl1
mov AX, adh2 ; Ziel
mov ES, AX
mov DI, adl2
movsb ; SI und DI wird in Abhängigkeit vom Direktion-Flag jeweils erhöht oder verringert.
Kombiniert man das mit dem Repeat-Behehl (rep) können so große Bereiche in einem Durchgang verarbeitet werden.
Dafür läd man zu Beginn in das CX-Register die Anzahl der Wiederholungen.
cld
... ; Laden der Segment und Offsetregister
...
mov AL, val
rep stosb ; Schreibt nach ES:DI den Inhalt von AL so oft wie im CX-Register angegeben wurde. Bei jedem Schritt wird DI erhöht und CX um eins veringert.
...
rep movsb ; Kopiert von DS:SI nach ES:DI soviele Bytes wie im CX-Register angegeben wurde. Bei jedem Schritt wird SI und DI erhöht und CX um eins veringert.
.................................
Zum Schluss mal ein praktisches Beispiel mit Schreibzugriff auf den VGA-Textbildschirm:
mov ax, 0B800h ; Segment-Adresse
mov es, ax
xor di, di ; Offset 0
mov cx, (80*25*2)/4 ; Anzahl
mov eax, 0F200F20h ; Farbe, ASCII, Farbe, ASCII
rep stosd
Optimierung für moderne x86:
Wenn bei einem Register nach einem Schreibzugriff darauf unmittelbar danach gleich ein Lesezugriff darauf erfolgt, dann gibt es eine Verzögerung.
So ist es ggf. sinnvoll die Befehle so umzustellen das keine Abhängigkeiten entstehen und man andere Befehle dazwischen plaziert. So eine gemischte Reihenfolge läßt sich dann schneller verarbeiten.
Auch wird der Code in Blöcken geladen, so das man so ein Block auch mit (unterschiedlichen) NOP-Befehle auffüllen kann und die Opcodes mit verscidener Anzahl an Bytes sich immer vollständig in so einem Block befinden und nicht auf zwei Blöcke verteilt sind.
Noch mehr Verzögerung bekommt man wenn man z.B. auf zwei oder vier Bytes im RAM gleichzeitig zugreifen möchte und sich die Adresse aber nicht durch zwei oder vier teilen läßt.
So ist darauf zu achten das die Ram-Zugriffe bestenfalls an einer geraden, oder durch 4 teilbaren Adresse vollzogen werden, wenn ein Word- oder DWord- Zugriff erfolgen soll.
Dirk