]> icculus.org git repositories - btb/d2x.git/blob - texmap/tmapppro.S
oops
[btb/d2x.git] / texmap / tmapppro.S
1 /// tmap_scanline_per - Pentium-Pro-optimized assembly version
2 /// written by Brian Raiter, Mar 1998.
3 /// lighting roundoff error fixed by Matt Mueller, July 1999
4
5 /// The gist of the algorithm is as follows (note that this is
6 /// pseudocode, not actual C):
7 ///
8 /// int  u = fx_u;
9 /// int  v = fx_v;
10 /// int  z = fx_z;
11 /// int  l = fx_l;
12 /// int  x, ubyz, vbyz;
13 /// byte texmap[64][64] = pixptr;
14 /// byte framebuffer[][bytes_per_row] = write_buffer;
15 /// byte lightingtable[][256] = gr_fade_table;
16 /// byte c;
17 ///
18 /// for (x = fx_xleft ; x <= fx_xright ; ++x) {
19 ///     ubyz = (u / z) & 63;
20 ///     vbyz = (v / z) & 63;
21 ///     c = texmap[ubyz][vbyz];
22 ///     if (c != TRANSPARENT_COLOR)
23 ///         framebuffer[fx_y][x] = lightingtable[l / 65536][c];
24 ///     u += fx_du_dx;
25 ///     v += fx_dv_dx;
26 ///     z += fx_dz_dx;
27 ///     l += fx_dl_dx;
28 /// }
29 ///
30 /// The global variable Transparency_on is zero when it is known that
31 /// there are no transparencies involved, so in that case we use a
32 /// different loop that skips the transparency test.
33 ///
34 /// The actual algorithm used here only does the division calculations
35 /// every fourth pixel, and linearly interpolates the other three.
36 /// Something along the lines of:
37 ///
38 /// /* Initial values as before */
39 /// int ubyz0, ubyz0, ubyz4, ubyz4, du1, dv1, i;
40 ///
41 /// ubyz0 = u / z;
42 /// vbyz0 = v / z;
43 /// for (x = fx_xleft ; x <= fx_xright - 3 ; x += 4) {
44 ///     u += fx_du_dx * 4;
45 ///     v += fx_dv_dx * 4;
46 ///     z += fx_dz_dx * 4;
47 ///     ubyz4 = u / z;
48 ///     vbyz4 = v / z;
49 ///     du1 = (ubyz4 - ubyz0) / 4;
50 ///     dv1 = (vbyz4 - vbyz0) / 4;
51 ///     ubyz = ubyz0;
52 ///     vbyz = vbyz0;
53 ///     for (i = 0 ; i < 4 ; ++i) {
54 ///         c = texmap[ubyz & 63][vbyz & 63];
55 ///         if (c != TRANSPARENT_COLOR)
56 ///             framebuffer[fx_y][x + i] = lightingtable[l / 65536][c];
57 ///         ubyz += du1;
58 ///         vbyz += dv1;
59 ///         l += fx_dl_dx;
60 ///     }
61 ///     ubyz0 = ubyz4;
62 ///     vbyz0 = vbyz4;
63 /// }
64 /// for ( ; x <= fx_xright ; ++x) {
65 ///     /* Finish off remaining 0-3 pixels */
66 /// }
67 ///
68 /// So much for the basic overview.
69 ///
70 /// In this version, the PPro's floating-point unit is pressed into
71 /// service to do the actual divisions, so that 1/z can be calculated
72 /// first, and the resulting reciprocal multiplied with u and v. These
73 /// two products are then stored back out as integers. This keeps us
74 /// down to doing only one division every four pixels, during which
75 /// other integer instructions can be overlapped. 
76 ///
77 /// The algorithm actually divides 64 by z, so that the rounded-off
78 /// products will effectively be stored with six fraction bits. This
79 /// allows the algorithm to correct for minor floating-point roundoff
80 /// errors. Two fraction bits are kept during the interpolation of the
81 /// three middle pixels, which hopefully increases the accuracy of the
82 /// approximations.
83 ///
84 /// We only need the lowest six (integral) bits of u/z and v/z for
85 /// each pixptr offset, so we only need eight bits of each fourth
86 /// pair of values to figure the interpolation. Add with the two
87 /// fractional bits we keep for extra precision flavor, this makes ten
88 /// bits for each value, or twenty to store the full pair. To simplify
89 /// the interpolation, the pair is packed into a single 32-bit
90 /// register like so:
91 ///
92 ///             3      2       1
93 ///             1      4       6       8       0
94 ///             vvVVVVVVvv____________uuUUUUUUuu
95 ///               \v&63/                \u&63/
96 ///
97 /// The unused bits between the u and v values permit the packed
98 /// values to be added/subtracted without the u values spilling over
99 /// into the v values. Then, the instructions "bswap %eax ; roll $6,
100 /// %eax ; andl $0x0FFF, %eax" will right-justify the desired values
101 /// into a pixptr offset.
102 ///
103 /// The FP stack is loaded up with the values of u, v, and z,
104 /// converted to floats. %ebp is used to hold the value of l, %esi is
105 /// is set to pixptr, and %edi points to our current position in
106 /// write_buffer.
107
108
109
110 // This is used to abbreviate an annoying external variable name.
111
112 .equ    fadetbl, _gr_fade_table
113
114
115 // The following macro encapsulates the floating-point instructions
116 // that put the results of a prior division to use and prepare for the
117 // next division. At the beginning of the macro, the FP stack contains
118 // (from top to bottom): 64/z, z, u, v. The macro computes (64*u)/z,
119 // which is stored in ubyz4, and (64*v)/z, which is stored in vybz4.
120 // Simultaneous with this, the macro adds dudx to u, dvdx to v, and
121 // dzdx to z, and finally puts 64 back onto the stack. At the end of
122 // the macro, the stack contains: 64, z, u, v.
123
124 .macro DoFPCalcs 0              // The FP stack after each instruction:
125                                 //               64/z  z    u    v
126         fst     %st(4)          //               64/z  z    u    v  64/z
127         fxch    %st(2)          //                 u   z  64/z   v  64/z
128         fmul    %st, %st(4)     // (64 * u) / z    u   z  64/z   v   u/z
129         fadds   (dudx)          // u += dudx       u'  z  64/z   v   u/z
130         fxch    %st(3)          //                 v   z  64/z   u'  u/z
131         fmul    %st, %st(2)     // (64 * v) / z     v   z   v/z   u'  u/z
132         fadds   (dvdx)          // v += dvdx       v'  z   v/z   u'  u/z
133         fxch    %st(1)          //                 z   v'  v/z   u'  u/z
134         fadds   (dzdx)          // z += dzdx       z'  v'  v/z   u'  u/z
135         fxch    %st(2)          //                v/z  v'   z'   u'  u/z
136         flds    (flt64)         //                 64 v/z   v'   z'   u'   u/z
137         fxch    %st(5)          //                u/z v/z   v'   z'   u'    64
138         fistpl  (ubyz4)         //                v/z  v'   z'   u'   64
139         fistpl  (vbyz4)         //                 v'  z'   u'   64
140         fxch    %st(3)          //                 64  z'   u'   v'
141                                 // (ready to start the next division)
142 .endm
143
144
145 #ifdef __linux__
146 .equ _pixptr, pixptr
147 .equ _gr_fade_table, gr_fade_table
148 .equ _write_buffer, write_buffer
149 .equ _bytes_per_row, bytes_per_row
150 .equ _fx_xleft, fx_xleft
151 .equ _fx_xright, fx_xright
152 .equ _fx_y, fx_y
153 .equ _fx_u, fx_u
154 .equ _fx_v, fx_v
155 .equ _fx_z, fx_z
156 .equ _fx_l, fx_l
157 .equ _fx_du_dx, fx_du_dx
158 .equ _fx_dv_dx, fx_dv_dx
159 .equ _fx_dz_dx, fx_dz_dx
160 .equ _fx_dl_dx, fx_dl_dx
161 .equ _Transparency_on, Transparency_on
162
163 .globl asm_ppro_tmap_scanline_per
164 #else
165 .globl _asm_ppro_tmap_scanline_per
166 #endif
167
168 .extern _pixptr, _gr_fade_table, _write_buffer
169 .extern _bytes_per_row, _fx_xleft, _fx_xright, _fx_y
170 .extern _fx_u, _fx_v, _fx_z, _fx_l
171 .extern _fx_du_dx, _fx_dv_dx, _fx_dz_dx, _fx_dl_dx
172 .extern _Transparency_on
173
174 //.local  dudx, dvdx, dzdx, dldx, l
175 //.local  ubyz, vbyz, uvzero
176 //.local  lastquartet, lastpixel, ctwl
177 //.local  flt64
178
179 .data
180
181 .balign 4
182
183 dudx:           .long   0               // u's rate of change as a float
184 dvdx:           .long   0               // v's rate of change as a float
185 dzdx:           .long   0               // z's rate of change as a float
186 dldx:           .long   0               // l's rate of change as an integer
187 l:              .long   0               // the current l value
188 ubyz4:          .long   0               // u/z for the next iteration
189 vbyz4:          .long   0               // v/z for the next iteration
190 uvzero:         .long   0               // packed u/z and v/z values
191 lastquartet:    .long   0               // where to stop the 4-pixels loop
192 lastpixel:      .long   0               // where to stop drawing entirely
193 flt64:          .long   0x42800000      // 64.0 (what we divide z into)
194 ctlwd:          .long   0               // the pre-tweaked FPU control word
195
196
197 .text
198
199 .balign 4
200
201 //
202 // void c_tmap_scanline_per(void)
203 //
204
205 #ifdef __linux__
206 asm_ppro_tmap_scanline_per:
207 #else
208 _asm_ppro_tmap_scanline_per:
209 #endif
210
211 // Save registers the compiler might be using.
212
213                 pushl   %ebp
214                 pushl   %edi
215                 pushl   %esi
216
217 // Kick the FPU into the lowest precision (still enough for our needs)
218 // so as to speed up fdiv.
219
220                 fnstcw  (ctlwd)
221                 movw    (ctlwd), %ax
222                 movl    %eax, %ebx
223                 andb    $0xFC, %bh
224                 movw    %bx, (ctlwd)
225                 fldcw   (ctlwd)
226                 movw    %ax, (ctlwd)
227
228 // Multiply dudx, dvdx, and dzdx by four, and store locally, converted
229 // into floating point.
230
231                 movl    (_fx_du_dx), %eax
232                 sall    $2, %eax
233                 movl    %eax, (dudx)
234                 movl    (_fx_dv_dx), %eax
235                 sall    $2, %eax
236                 movl    %eax, (dvdx)
237                 movl    (_fx_dz_dx), %eax
238                 sall    $2, %eax
239                 movl    %eax, (dzdx)
240                 fildl   (dudx)
241                 fildl   (dvdx)
242                 fildl   (dzdx)
243                 fxch    %st(2)
244                 fstps   (dudx)
245                 fstps   (dvdx)
246                 fstps   (dzdx)
247
248 // bytes_per_row * fx_y is the offset for the current scanline. (We do
249 // this now before we start the first FP division.)
250
251                 movl    (_bytes_per_row), %eax
252                 xorl    %edx, %edx
253                 mull    (_fx_y)
254
255 // Push v, u, z, and 64.0 onto the FPU stack, and then start
256 // calculating the first 64 / z.
257
258                 fildl   (_fx_v)
259                 fildl   (_fx_u)
260                 fildl   (_fx_z)
261                 flds    (flt64)
262                 fdiv    %st(1)
263
264 // Meanwhile, get l and dldx (again, the latter multiplied by four).
265 // l will be stored in %ebp for the duration. The original values are
266 // divided by 256 so that the byte needed for the fade table offset
267 // will be aligned.
268
269 //Dividing by 256 is bad.. rounding errors and crap.  We'll now do that
270 //right before we need to access the table instead.  -MM
271
272                 movl    (_fx_l), %edx
273 //              sarl    $8, %edx
274                 movl    %edx, (l)
275                 movl    (_fx_dl_dx), %edx
276 //              sarl    $6, %edx
277                 sall    $2, %edx
278                 movl    %edx, (dldx)
279
280 // Store pixptr, the pointer to our 64x64 texture map, in %esi. Store
281 // write_buffer, the pointer to our frame buffer, in %edi. Then offset
282 // %edi so that it points to pixel [fx_y][fx_xleft]. Calculate a
283 // pointer to [fx_y][fx_xright + 1] so we know when to stop drawing.
284 // Also calculate a pointer to [fx_y][(fx_xright + 1) & ~3] so we know
285 // when to stop drawing four pixels at a time.
286
287                 movl    (_pixptr), %esi
288                 movl    (_write_buffer), %edi
289                 movl    (_fx_xright), %ecx
290                 addl    %eax, %edi
291                 incl    %ecx
292                 addl    %edi, %ecx
293                 movl    %ecx, (lastpixel)
294                 addl    (_fx_xleft), %edi
295                 movl    %ecx, %eax
296                 subl    %edi, %eax
297                 jle     LeaveNow
298                 andl    $3, %eax
299                 subl    %eax, %ecx
300                 movl    %ecx, (lastquartet)
301
302 // Calculate round(64 * u / z) and round(64 * v / z), store, and
303 // increment u, v, and z. Then start calculating the second 64 / z.
304
305                 DoFPCalcs
306                 fdiv    %st(1)
307
308 // Get our u/z and v/z values, lop off the bits we don't care
309 // about, pack, and store in uvzero.
310
311                 movl    (ubyz4), %eax
312                 incl    %eax
313                 andl    $0x3FF0, %eax
314                 shrl    $4, %eax
315                 movl    (vbyz4), %ebx
316                 incl    %ebx
317                 andl    $0x3FF0, %ebx
318                 shll    $18, %ebx
319                 orl     %eax, %ebx
320                 movl    %ebx, (uvzero)
321
322 // Are there at least four pixels to draw? If not, skip to the epilog
323 // code.
324
325                 cmpl    %ecx, %edi
326                 je      LastBits
327
328 // Do we need to test for transparencies?
329
330                 testl   $(~0), (_Transparency_on)
331                 jnz     LoopTransOn
332
333 // If not, then use the simpler loop here.
334
335 .balign 4
336
337 LoopTransOff:
338
339 // While the FPU is busy dividing, the latest u/z and v/z values are
340 // retrieved, packed, and stored in uvzero (to be used again in the
341 // next iteration). The old uvzero value, which contains the uv values
342 // for pixel 0, gets subtracted from the new uvzero value to
343 // determined the total change in u/z and v/z across the four pixels,
344 // and this is divided by 4 to get the average. This average is then
345 // used to estimate the values for pixels 1, $2, and 3. The old uvzero
346 // value is used immediately to calculate pixel 0, while %eax, %ebx, and
347 // %ecx are entrusted with the uv values for pixels 1, $2, and 3
348 // respectively, while %edx is our "cleansed" register for using byte
349 // values as memory pointer offsets. %ebp is loaded with the high byte
350 // of l, forming half of the offset for the fade table lookup. (The
351 // pixel from the texture-map bitmap supplies the other half.) Each
352 // value is used to set its pixel as follows (assuming %eax holds our
353 // packed uv value):
354 //
355 //      a:      bswapl  %eax                            / move u and v to the
356 //      b:      roll    $6, %eax                        /   far right
357 //      c:      andl    $0x0FFF, %eax                   / mask off extra bits
358 //      d:      movb    (%esi,%eax), %dl                / get texture-map pixel
359 //      e:      movb    fadetbl(%edx,%ebp), %dl         / correct for lighting
360 //      f:      movb    %dl, (%edi)                     / write to frame buffer
361 //
362 // The above is done four times, once for each pixel. Some of the
363 // calculations may appear to be interleaved haphazardly, but the PPro
364 // seems to like it this way.
365
366                 DoFPCalcs
367                 fdiv    %st(1)
368
369                 xorl    %edx, %edx
370                 movl    (uvzero), %eax                  // %eax = uv for pixel 0
371                 bswapl  %eax                            // 0 a
372                 roll    $6, %eax                        // 0 b
373                 andl    $0x0FFF, %eax                   // 0 c
374                 movb    (%esi,%eax), %dl                // 0 d
375                 movl    (l), %ebp
376                 movl    (dldx), %ecx
377                 addl    %ebp, %ecx
378                 movl    %ecx, (l)
379                 sarl    $8, %ebp
380                 andl    $0x7F00, %ebp
381         movb    fadetbl(%edx,%ebp), %dl         // 0 e
382
383                 movl    (vbyz4), %ebx
384                 incl    %ebx
385                 andl    $0x3FF0, %ebx
386                 movl    (ubyz4), %ecx
387                 shll    $18, %ebx
388                 incl    %ecx
389                 andl    $0x3FF0, %ecx
390                 shrl    $4, %ecx
391                 movl    (uvzero), %eax
392                 orl     %ebx, %ecx
393                 movl    %ecx, (uvzero)
394                 orl     $0x1000, %ecx
395                 subl    %eax, %ecx
396                 shrl    $2, %ecx
397
398                 movb    %dl, (%edi)                     // 0 f
399                 lea     (%eax,%ecx,2), %ebx             // %ebx = uv for pixel 2
400                 addl    %ecx, %eax                      // %eax = uv for pixel 1
401                 bswapl  %eax                            // 1 a
402                 roll    $6, %eax                        // 1 b
403                 addl    %ebx, %ecx                      // %ecx = uv for pixel 3
404                 bswapl  %ebx                            // 2 a
405                 roll    $6, %ebx                        // 2 b
406                 bswapl  %ecx                            // 3 a
407                 andl    $0x0FFF, %eax                   // 1 c
408                 andl    $0x0FFF, %ebx                   // 2 c
409                 roll    $6, %ecx                        // 3 b
410
411                 movb    (%esi,%eax), %dl                // 1 d
412                 movb    fadetbl(%edx,%ebp), %al         // 1 e
413                 movb    (%esi,%ebx), %dl                // 2 d
414                 movb    fadetbl(%edx,%ebp), %bl         // 2 e
415                 movb    %al, 1(%edi)                    // 1 f
416                 andl    $0x0FFF, %ecx                   // 3 c
417                 movb    %bl, 2(%edi)                    // 2 f
418                 movb    (%esi,%ecx), %dl                // 3 d
419                 movb    fadetbl(%edx,%ebp), %cl         // 3 e
420                 movb    %cl, 3(%edi)                    // 3 f
421
422                 addl    $4, %edi
423                 cmpl    (lastquartet), %edi
424                 jl      LoopTransOff
425
426 // Are there any pixels left at all?
427
428                 cmpl    (lastpixel), %edi
429                 jne     LastBits
430                 jmp     LeaveNow
431
432
433 .balign 4
434
435 LoopTransOn:
436
437 // This is similar to the LoopTransOff loop, the big change being that
438 // each value retrieved from the texture map is tested against 255,
439 // the transparent "color". A value of 255 in the texture map means to
440 // let the existing value for that pixel in write_buffer go by
441 // unchanged. Thus the code for each pixel looks something like this
442 // instead:
443 //
444 //      a:      bswapl  %eax                            / move u and v to the
445 //      b:      roll    $6, %eax                        /   far right
446 //      c:      andl    $0x0FFF, %eax                   / mask off extra bits
447 //      d:      movb    (%esi,%eax), %dl                / get texture-map pixel
448 //      e:      cmpb    $255, %dl                       / is pixel transparent?
449 //      f:      sbbb    %ah, %ah                        / yes:%ah=00, no:%ah=FF
450 //      g:      movb    fadetbl(%edx,%ebp), %dl         / correct for lighting
451 //      h:      movb    (%edi), %al                     / get current pixel
452 //      i:      xorb    %al, %dl                        / combine the two
453 //      j:      andb    %dl, %ah                        / use %ah as a mask to
454 //      k:      xorb    %ah, %al                        /   select which pixel
455 //      l:      movb    %al, (%edi)                     / write to frame buffer
456 // 
457 // When the texture-map value is 255, the code simply writes the
458 // original frame-buffer value back out again; otherwise the new pixel
459 // is written instead. The ands and xors used to accomplish this bulk
460 // up the code, but on the whole it is better than having four
461 // unpredictable jumps in the loop.
462
463                 DoFPCalcs
464                 fdiv    %st(1)
465
466                 movl    (uvzero), %eax                  // %eax = uv for pixel 0
467                 bswapl  %eax                            // 0 a
468                 movl    (dldx), %ecx
469                 movl    (l), %ebp
470                 addl    %ebp, %ecx                      
471                 roll    $6, %eax                        // 0 b
472                 andl    $0x0FFF, %eax                   // 0 c
473                 xorl    %edx, %edx
474                 movb    (%esi,%eax), %dl                // 0 d
475                 cmpb    $255, %dl                       // 0 e
476                 sbbb    %ah, %ah                        // 0 f
477                 movl    %ecx, (l)
478                 sarl    $8, %ebp
479                 andl    $0x7F00, %ebp
480
481                 movb    fadetbl(%edx,%ebp), %dl         // 0 g
482                 movb    (%edi), %al                     // 0 h
483                 xorb    %al, %dl                        // 0 i
484                 andb    %dl, %ah                        // 0 j
485                 xorb    %ah, %al                        // 0 k
486                 movb    %al, (%edi)                     // 0 l
487
488                 movl    (vbyz4), %ebx
489                 movl    (ubyz4), %ecx
490                 incl    %ebx
491                 andl    $0x3FF0, %ebx
492                 incl    %ecx
493                 andl    $0x3FF0, %ecx
494                 shll    $18, %ebx
495                 shrl    $4, %ecx
496                 orl     %ebx, %ecx
497                 movl    (uvzero), %eax
498                 movl    %ecx, (uvzero)
499                 orl     $0x1000, %ecx
500                 subl    %eax, %ecx
501                 shrl    $2, %ecx
502
503                 lea     (%eax,%ecx,2), %ebx             // %ebx = uv for pixel 2
504                 addl    %ecx, %eax                      // %eax = uv for pixel 1
505                 bswapl  %eax                            // 1 a
506                 roll    $6, %eax                        // 1 b
507                 addl    %ebx, %ecx                      // %ecx = uv for pixel 3
508                 bswapl  %ebx                            // 2 a
509                 roll    $6, %ebx                        // 2 b
510                 andl    $0x0FFF, %eax                   // 1 c
511                 movb    (%esi,%eax), %dl                // 1 d
512                 cmpb    $255, %dl                       // 1 e
513                 sbbb    %ah, %ah                        // 1 f
514                 bswapl  %ecx                            // 3 a
515                 movb    1(%edi), %al                    // 1 h
516                 movb    fadetbl(%edx,%ebp), %dl         // 1 g
517
518                 roll    $6, %ecx                        // 3 b
519                 andl    $0x0FFF, %ebx                   // 2 c
520                 xorb    %al, %dl                        // 1 i
521                 andb    %dl, %ah                        // 1 j
522                 movb    (%esi,%ebx), %dl                // 2 d
523                 cmpb    $255, %dl                       // 2 e
524                 sbbb    %bh, %bh                        // 2 f
525                 movb    fadetbl(%edx,%ebp), %dl         // 2 g
526                 andl    $0x0FFF, %ecx                   // 3 c
527                 movb    2(%edi), %bl                    // 2 h
528                 xorb    %bl, %dl                        // 2 i
529                 andb    %dl, %bh                        // 2 j
530                                                         
531                 movb    (%esi,%ecx), %dl                // 3 d
532                 cmpb    $255, %dl                       // 3 e
533                 sbbb    %ch, %ch                        // 3 f
534                 movb    3(%edi), %cl                    // 3 h
535                 movb    fadetbl(%edx,%ebp), %dl         // 3 g
536                 xorb    %cl, %dl                        // 3 i
537                 andb    %dl, %ch                        // 3 j
538
539                 xorb    %ah, %al                        // 1 k
540                 movb    %al, 1(%edi)                    // 1 l
541                 xorb    %bh, %bl                        // 2 k
542                 movb    %bl, 2(%edi)                    // 2 l
543                 xorb    %ch, %cl                        // 3 k
544                 movb    %cl, 3(%edi)                    // 3 l
545
546                 addl    $4, %edi
547                 cmpl    (lastquartet), %edi
548                 jl      LoopTransOn
549
550 // Quit if there are none at all left.
551
552                 cmpl    (lastpixel), %edi
553                 je      LeaveNow
554
555
556 LastBits:
557
558 // Here we finish off the last one-to-three pixels assigned to us.
559 // Rather than calculating values for all four pixels, we just divide
560 // the difference by four and keep adding this average into the value
561 // as needed. (This code is not particularly optimized, by the way,
562 // since it represents such a miniscule amount of the running time.)
563
564                 DoFPCalcs
565                 movl    (l), %ebp
566                 sarl    $8, %ebp
567                 andl    $0x7F00, %ebp
568                 movl    (ubyz4), %eax
569                 incl    %eax
570                 andl    $0x3FF0, %eax
571                 shrl    $4, %eax
572                 movl    (vbyz4), %ecx
573                 incl    %ecx
574                 andl    $0x3FF0, %ecx
575                 shll    $18, %ecx
576                 orl     %eax, %ecx
577                 movl    (uvzero), %ebx
578                 orl     $0x1000, %ecx
579                 subl    %ebx, %ecx
580                 shrl    $2, %ecx
581                 xorl    %edx, %edx
582
583 LoopLastBits:   movl    %ebx, %eax
584                 bswapl  %eax
585                 roll    $6, %eax
586                 andl    $0x0FFF, %eax
587                 movb    (%esi,%eax), %dl
588                 cmpb    $255, %dl
589                 je      LetPixelBy
590                 movb    fadetbl(%edx,%ebp), %dl
591                 movb    %dl, (%edi)
592 LetPixelBy:     incl    %edi
593                 addl    %ecx, %ebx
594                 cmpl    (lastpixel), %edi
595                 jl      LoopLastBits
596
597
598 LeaveNow:
599
600 // We're done! Clear the stacks, reset the FPU control word, and we
601 // are so out of here.
602
603                 popl    %esi
604                 popl    %edi
605                 popl    %ebp
606                 fcompp
607                 fcompp
608                 fldcw   (ctlwd)
609                 ret