Hi
Kann man mir bitte mal Teile des nachfolgenden Assembler-Code (auch gerne den Ganzen) aus der System.pas erklären ?
Es ist ja soweit ersichtlich, dass dort eine Division zweier 64-Bit-Integer durchgeführt wird, aber ich verstehe den Sinn einiger Befehlsfolgen nicht.
Ich habe zwar eine komplette Referenz aller (Intel-)Assembler-Befehle mit Erklärungen, an deren Verständnis es nicht hakt, aber Ich bin kein Assembler-Profi und kein Mathematik-Spezi.
Die Prozedur "__lldiv" ist ja eine Software-Umsetzung für Divisionen von 64-Bit-Integern auf 32-Bit-CPUs. Demnach müsste sie sich ja auf größere Zahlen erweitern lassen. Natürlich müssten die Zahlen dann in einem Array gespeichert werden (
imho), z.B. einem array [0..3] of DWORD für 128-Bit-Zahlen.
Also bei mir hapert es jetzt an drei Stellen:
1. am Verständnis einiger Assembler-Befehle
2. am Verständnis einiger Assembler-Befehlsfolgen
3. an der Erweiterung auf größere Zahlen in Array-Form
Ich suche also nach einer allgemeinen Erklärung des Ablaufs dieser Prozedur,
und, wenn dann noch nötig, nach Hilfe für eine Erweiterung.
Ich suche NICHT nach einer Bibliothek, die mir das Lernen abnimmt.
Ich hoffe, man kann mir weiterhelfen:
1.:
Was sagt mit diese Zeile:
mov ebx,20[esp]
Ich kenne nur solche Befehle:
2:
Warum werden hier die Register edx, esi und edi um 1 über das Carry hinweg rotiert:
Delphi-Quellcode:
@__lldiv@xloop:
shl eax,1
rcl edx,1
rcl esi,1
rcl edi,1
Das waren wahrscheinlich nur die ersten Fragen...
Delphi-Quellcode:
// ------------------------------------------------------------------------------
// 64-bit signed division
// ------------------------------------------------------------------------------
//
// Dividend = Numerator, Divisor = Denominator
//
// Dividend(EAX:EDX), Divisor([ESP+8]:[ESP+4]) ; before reg pushing
//
//
procedure __lldiv;
asm
push ebp
push ebx
push esi
push edi
xor edi,edi
mov ebx,20[esp]
// get the divisor low dword
mov ecx,24[esp]
// get the divisor high dword
or ecx,ecx
jnz @__lldiv@slow_ldiv
// both high words are zero
or edx,edx
jz @__lldiv@quick_ldiv
or ebx,ebx
jz @__lldiv@quick_ldiv
// if ecx:ebx == 0 force a zero divide
// we don't expect this to actually
// work
@__lldiv@slow_ldiv:
// Signed division should be done. Convert negative
// values to positive and do an unsigned division.
// Store the sign value in the next higher bit of
// di (test mask of 4). Thus when we are done, testing
// that bit will determine the sign of the result.
or edx,edx
// test sign of dividend
jns @__lldiv@onepos
neg edx
neg eax
sbb edx,0
// negate dividend
or edi,1
@__lldiv@onepos:
or ecx,ecx
// test sign of divisor
jns @__lldiv@positive
neg ecx
neg ebx
sbb ecx,0
// negate divisor
xor edi,1
@__lldiv@positive:
mov ebp,ecx
mov ecx,64
// shift counter
push edi
// save the flags
//
// Now the stack looks something like this:
//
// 24[esp]: divisor (high dword)
// 20[esp]: divisor (low dword)
// 16[esp]: return EIP
// 12[esp]: previous EBP
// 8[esp]: previous EBX
// 4[esp]: previous ESI
// [esp]: previous EDI
//
xor edi,edi
// fake a 64 bit dividend
xor esi,esi
@__lldiv@xloop:
shl eax,1
// shift dividend left one bit
rcl edx,1
rcl esi,1
rcl edi,1
cmp edi,ebp
// dividend larger?
jb @__lldiv@nosub
ja @__lldiv@subtract
cmp esi,ebx
// maybe
jb @__lldiv@nosub
@__lldiv@subtract:
sub esi,ebx
sbb edi,ebp
// subtract the divisor
inc eax
// build quotient
@__lldiv@nosub:
loop @__lldiv@xloop
//
// When done with the loop the four registers values' look like:
//
// | edi | esi | edx | eax |
// | remainder | quotient |
//
pop ebx
// get control bits
test ebx,1
// needs negative
jz @__lldiv@finish
neg edx
neg eax
sbb edx,0
// negate
@__lldiv@finish:
pop edi
pop esi
pop ebx
pop ebp
ret 8
@__lldiv@quick_ldiv:
div ebx
// unsigned divide
xor edx,edx
jmp @__lldiv@finish
end;