Potenzieren(Basis 2) in einer 16Bit-Umgebung 3. Juli 2010

Ich hab mich in letzter Zeit nochmal was mit den Grundsätzen der Betriebssystemprogrammierung auseinandergesetzt.
Den Bootloader hab ich, jetzt ist es Zeit für den “Kernel”, alles jedoch noch ganz ganz einfach gehalten.
Also pack ich mir die Interrupt-Liste und schaue, was ich für Befehle denn schreiben könnte.
Oh, da, das scheint Interessant! Interrupt Nr 0×15, ah = 0×86
Sleep! Sowas brauch man ja quasi immer.
Jedoch kommt jetzt das Problem, was will man mit einem Sleep in Nano-Sekunden?
Ich benötige also eine Funktion, die Milli-Sekunden “schläft”, d.h. * 1000
Der größte Wert, den ein Register im 16-Bitmodus beinhalten kann, ist 0xFFFF => 65.535,
was bedeutet, dass man mit einem Register in nanosec-Modus nur 65 ms warten kann.
Dafür akzeptiert das Bios 2 Register, damit man bis 0xFFFFFFFF sleepen kann. Soviel zu den Grundlagen, wo liegt denn jetzt das Problem ?
Sagen wir wir wollen 1.000 ms warten ( 1 = Sek ) -> 0x3E8. Diese Zahl umgewandelt in nano-Sekunden (*1000) = 1.000.000 -> 0xF4240
OOPS! Wir kommen mit einem Register nicht aus!
Nun müssen wir irgendwie einen Weg finden, von einem Register, welches in Milli-Sekunden angegeben ist, 2 Register in Nano-Sekunden zu zaubern.
Also müssen wir jetzt erstmal überlegen. *1000 ist Grundsätzlich eher schlecht, jedoch wäre ein * 1024 viel einfacher zu realisieren.
Erstmal beachten wir die Registergrenzen nicht und gehen von einem 32 Bit-System aus. (Doppelte Registergröße)
Bei einem * 1024 müsste man nur die Bits von dem Register um 10 Bits verschieben (2 ^10 = 1024).
Dies Bedeutet (für den theoretisch größten Wert von unserem Milli-Sekunden-Register):

|OBERER REGISTER | UNTERER REGISTER |
|00000000 00000000| 11111111 11111111| = 0x000000FF = 255
Nun verschieben wir die Bits um 10 Stellen nach links.

|OBERER REGISTER | UNTERER REGISTER |
|00000011 11111111 | 11111100 00000000| = 0x00003FC0 = 261120

Also unseren * 1024 haben wir bereits. Jetzt kümmern wir uns um die Registergrenzen.
Wie wir oben sehen (wir haben ja den Extremfall genommen), hat der obere Register 10 Bits des unteren Registers bekommen,
und zwar “schwappen” die 10 oberen Bits des unteren Registers über zum oberen Register.
(Ich glaub das klingt jetzt ziemlich verwirrend aber ich hoffe ihr wisst, was ich meine)
Da im 16 Bit-Modus die Register nur 16 Bits lang sind, haben wir nur das untere Register, also gehen uns die ersten 10 Bits verloren.
Um dies zu verhindern, “sichern” wir diese erstmal:
(ax = Register, wo zu beginn die Millisekunden stehen, nachher stehen die Nanosekunden in cx + dx, also in cx sind die oberen 10 Bits, in dx die unteren)
ax & 1111111111000000 = obere 10 Bits
cx = ax & 0xFFC0

Da wir durch das rüberkopieren die oberen 10 Bits von cx gesetzt haben, müssen wir diese wieder um 6 Bits nach rechts verschieben, da wir sonst eine 16-Bit-Verschiebung hätten:
cx = cx >> 6
Oder kurz:
cx = (ax & 0xFFC0) >> 6

Und jetzt sind wir auch schon fast fertig, jetzt können wir nämlich endlich ax um 10 bits verschieben.
dx = ax << 10

Und nun ist das Ergebnis in cx : dx

Also nochmal abschließend, meine Sleepfunktion:

sleep:
pushad
mov cx, ax
and cx, 0xFFC0 ;CX -> Die oberen 10 Bits
shr cx, 6 ; CX -> Die 10 Bits nach unten verschieben
shl ax, 0x0A ; AX -> Verschieben um 10 Bits nach links => * 1024
mov dx, ax
mov ah, 0×86
int 0×15
popad
ret

Noch kurz erklärt
=>
ax = 11111111 11111111 ( = 0xFFFF )
mov cx, ax
cx = 11111111 11111111
AND 11111111 11000000;and cx, 0xFFC0
cx = 11111111 11000000
shr cx, 6
cx = 00000011 11111111

shl ax, 0x0A
ax = 11111100 00000000
mov dx, ax
dx = 11111100 00000000

cx:dx
00000011 11111111 11111100 00000000
= 0x3FFFC00
0x3FFFC00 / 1024 => 0xFFFF

WIN! :)

One Comments
Stammi Juli 3rd, 2010

Respekt! Super beschrieben :)
Besser hätte man es nicht machen können.

Vielen Dank.

Leave a Reply