-
Notifications
You must be signed in to change notification settings - Fork 0
/
H_LDIV.ASM
227 lines (207 loc) · 6.36 KB
/
H_LDIV.ASM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
;[]-----------------------------------------------------------------[]
;| H_LDIV.ASM -- long division routine |
;| |
;| C/C++ Run Time Library Version 4.0 |
;| |
;| Copyright (c) 1987, 1991 by Borland International Inc. |
;| All Rights Reserved. |
;[]-----------------------------------------------------------------[]
.model medium
INCLUDE RULES.ASI
.386C ;JAB - we use 386 instructions
_TEXT segment public byte 'CODE'
assume cs:_TEXT
public LDIV@
public F_LDIV@
public N_LDIV@
public LUDIV@
public F_LUDIV@
public N_LUDIV@
public LMOD@
public F_LMOD@
public N_LMOD@
public LUMOD@
public F_LUMOD@
public N_LUMOD@
N_LDIV@:
pop cx ;fix up far return
push cs
push cx
LDIV@:
F_LDIV@:
xor cx,cx ; signed divide
jmp short common
; JAB
;
; If we're using a 386 or better, the two instructions above get patched
; to be NOP's (4 of them). So, instead of using the looping code,
; we use the 386's long divide instruction.
;
; The stack after setting up the stack frame:
; 12[bp]: divisor (high word)
; 10[bp]: divisor (low word)
; 8[bp]: dividend (high word)
; 6[bp]: dividend (low word)
; 4[bp]: return CS
; 2[bp]: return IP
; 0[bp]: previous BP
;
IDEAL
push bp
mov bp,sp ;Save BP, and set it equal to stack
mov eax,[DWORD PTR bp+6]
cdq
idiv [DWORD PTR bp+10]
mov edx,eax
shr edx,16
pop bp ;Restore BP
retf 8 ;Return to original caller
MASM
N_LUDIV@:
pop cx ;fix up far return
push cs
push cx
LUDIV@:
F_LUDIV@:
mov cx,1 ; unsigned divide
jmp short common
N_LMOD@:
pop cx ;fix up far return
push cs
push cx
LMOD@:
F_LMOD@:
mov cx,2 ; signed remainder
jmp short common
N_LUMOD@:
pop cx ;fix up far return
push cs
push cx
LUMOD@:
F_LUMOD@:
mov cx,3 ; unsigned remainder
;
; di now contains a two bit control value. The low order
; bit (test mask of 1) is on if the operation is unsigned,
; signed otherwise. The next bit (test mask of 2) is on if
; the operation returns the remainder, quotient otherwise.
;
common:
push bp
push si
push di
mov bp,sp ; set up frame
mov di,cx
;
; dividend is pushed last, therefore the first in the args
; divisor next.
;
mov ax,10[bp] ; get the first low word
mov dx,12[bp] ; get the first high word
mov bx,14[bp] ; get the second low word
mov cx,16[bp] ; get the second high word
or cx,cx
jnz slow@ldiv ; both high words are zero
or dx,dx
jz quick@ldiv
or bx,bx
jz quick@ldiv ; if cx:bx == 0 force a zero divide
; we don't expect this to actually
; work
slow@ldiv:
test di,1 ; signed divide?
jnz positive ; no: skip
;
; 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 dx,dx ; test sign of dividend
jns onepos
neg dx
neg ax
sbb dx,0 ; negate dividend
or di,0Ch
onepos:
or cx,cx ; test sign of divisor
jns positive
neg cx
neg bx
sbb cx,0 ; negate divisor
xor di,4
positive:
mov bp,cx
mov cx,32 ; shift counter
push di ; save the flags
;
; Now the stack looks something like this:
;
; 16[bp]: divisor (high word)
; 14[bp]: divisor (low word)
; 12[bp]: dividend (high word)
; 10[bp]: dividend (low word)
; 8[bp]: return CS
; 6[bp]: return IP
; 4[bp]: previous BP
; 2[bp]: previous SI
; [bp]: previous DI
; -2[bp]: control bits
; 01 - Unsigned divide
; 02 - Remainder wanted
; 04 - Negative quotient
; 08 - Negative remainder
;
xor di,di ; fake a 64 bit dividend
xor si,si ;
xloop:
shl ax,1 ; shift dividend left one bit
rcl dx,1
rcl si,1
rcl di,1
cmp di,bp ; dividend larger?
jb nosub
ja subtract
cmp si,bx ; maybe
jb nosub
subtract:
sub si,bx
sbb di,bp ; subtract the divisor
inc ax ; build quotient
nosub:
loop xloop
;
; When done with the loop the four register value look like:
;
; | di | si | dx | ax |
; | remainder | quotient |
;
pop bx ; get control bits
test bx,2 ; remainder?
jz usequo
mov ax,si
mov dx,di ; use remainder
shr bx,1 ; shift in the remainder sign bit
usequo:
test bx,4 ; needs negative
jz finish
neg dx
neg ax
sbb dx,0 ; negate
finish:
pop di
pop si
pop bp
retf 8
quick@ldiv:
div bx ; unsigned divide
; DX = remainder AX = quotient
test di,2 ; want remainder?
jz quick@quo
xchg ax,dx
quick@quo:
xor dx,dx
jmp short finish
_TEXT ends
end