854e5f1a6376a7d5914e6848caf4febb9cb55dae
[btb/d2x.git] / 2d / linear.asm
1 ; $Id: linear.asm,v 1.3 2002-07-17 21:55:19 bradleyb Exp $
2 ;THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 ;SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 ;END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ;ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 ;IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 ;SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 ;FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 ;CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 ;AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 ;COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 ;
13 ;
14 ; Routines to access linear VGA memory
15 ;
16 ; Old Log:
17 ;
18 ; Revision 1.20  1994/11/28  17:08:30  john
19 ; Took out some unused functions in linear.asm, moved
20 ; gr_linear_movsd from linear.asm to bitblt.c, made sure that
21 ; the code in ibiblt.c sets the direction flags before rep movsing.
22
23 ; Revision 1.19  1994/11/27  22:57:56  john
24 ; Took out some code that was never called.
25
26 ; Revision 1.18  1994/09/12  14:40:16  john
27 ; Neatend.
28
29 ; Revision 1.17  1994/07/27  18:30:30  john
30 ; Took away the blending table.
31
32 ; Revision 1.16  1994/04/08  16:59:28  john
33 ; Add fading poly's; Made palette fade 32 instead of 16.
34
35 ; Revision 1.15  1993/12/21  20:10:03  john
36 ; *** empty log message ***
37
38 ; Revision 1.14  1993/12/21  19:58:31  john
39 ; added selector stuff
40
41 ; Revision 1.13  1993/12/21  11:40:51  john
42 ; *** empty log message ***
43
44 ; Revision 1.12  1993/12/09  15:01:52  john
45 ; Changed palette stuff majorly
46
47 ; Revision 1.11  1993/12/08  16:41:02  john
48 ; *** empty log message ***
49
50 ; Revision 1.10  1993/12/08  11:50:17  john
51 ; Fixed bug with gr_init
52
53 ; Revision 1.9  1993/12/07  12:32:12  john
54 ; moved bmd_palette to gr_palette
55
56 ; Revision 1.8  1993/12/03  12:11:25  john
57 ; *** empty log message ***
58
59 ; Revision 1.7  1993/11/16  11:29:08  john
60 ; *** empty log message ***
61
62 ; Revision 1.6  1993/10/15  16:22:13  john
63 ; *** empty log message ***
64
65 ; Revision 1.5  1993/09/29  16:15:28  john
66 ; added assembler linear_line
67
68 ; Revision 1.4  1993/09/26  18:59:27  john
69 ; fade stuff
70
71 ; Revision 1.3  1993/09/21  14:00:59  john
72 ; added code to save 43/50 line text modes.
73
74 ; Revision 1.2  1993/09/16  17:28:06  john
75 ; added code to save/restore video mode
76
77 ; Revision 1.1  1993/09/08  11:41:30  john
78 ; Initial revision
79
80 ;
81 ;
82
83
84 [BITS 32]
85
86 section .data
87                 ; Put data here
88 %ifdef __linux__
89 %define _gr_var_color gr_var_color
90 %define _gr_var_bitmap gr_var_bitmap
91 %define _gr_var_bwidth gr_var_bwidth
92 %endif
93                 
94                 global _gr_var_color
95                 global _gr_var_bitmap
96                 global _gr_var_bwidth
97                 _gr_var_color   dd  0
98                 _gr_var_bitmap  dd  0
99                 _gr_var_bwidth  dd  0
100
101                 ; Local variables for gr_linear_line_
102                 AdjUp           dd  0   ;error term adjust up on each advance
103                 AdjDown         dd  0   ;error term adjust down when error term turns over
104                 WholeStep       dd  0   ;minimum run length
105                 XAdvance        dd  0   ;1 or -1, for direction in which X advances
106                 XStart          dd  0
107                 YStart          dd  0
108                 XEnd            dd  0
109                 YEnd            dd  0
110
111 section .text
112 ; Fast run length slice line drawing implementation for mode 0x13, the VGA's
113 ; 320x200 256-color mode.
114 ; Draws a line between the specified endpoints in color Color.
115
116
117 global _gr_linear_line
118 global gr_linear_line
119 _gr_linear_line:
120 gr_linear_line:
121
122         cld
123
124         push    ebx  ;preserve C register variables
125         push    esi
126         push    edi
127         push    ebp
128
129         mov     eax,[esp+20]
130         mov     edx,[esp+24]
131         mov     ebx,[esp+28]
132         mov     ecx,[esp+32]
133         mov     [XStart], eax
134         mov     [YStart], edx
135         mov     [XEnd], ebx
136         mov     [YEnd], ecx
137
138         mov     ebp, [_gr_var_bwidth]
139
140 ; We'll draw top to bottom, to reduce the number of cases we have to handle,
141 ; and to make lines between the same endpoints always draw the same pixels.
142
143         mov     eax,[YStart]
144         cmp     eax,[YEnd]
145         jle     LineIsTopToBottom
146         xchg    [YEnd], eax    ;swap endpoints
147         mov     [YStart], eax
148         mov     ebx, [XStart]
149         xchg    [XEnd], ebx
150         mov     [XStart], ebx
151
152 LineIsTopToBottom:
153
154 ; Point EDI to the first pixel to draw.
155         mov     edx,ebp
156         mul     edx             ;[YStart] * ebp
157         mov     esi, [XStart]
158         mov     edi, esi
159         add     edi, [_gr_var_bitmap]
160         add     edi, eax        ;EDI = [YStart] * ebp + [XStart]
161                                                         ; = offset of initial pixel
162
163 ; Figure out how far we're going vertically (guaranteed to be positive).
164         mov     ecx, [YEnd]
165         sub     ecx, [YStart]     ;ECX = YDelta
166
167 ; Figure out whether we're going left or right, and how far we're going
168 ; horizontally. In the process, special-case vertical lines, for speed and
169 ; to avoid nasty boundary conditions and division by 0.
170
171         mov     edx, [XEnd]
172         sub     edx, esi        ;XDelta
173         jnz     NotVerticalLine ;XDelta == 0 means vertical line
174                                                         ;it is a vertical line
175                                                         ;yes, special case vertical line
176
177         mov     eax, [_gr_var_color]
178 VLoop:
179         mov     [edi],al
180         add     edi,ebp
181         dec     ecx
182         jns     VLoop
183         jmp     Done
184
185 ; Special-case code for horizontal lines.
186
187 IsHorizontalLine:
188         mov     eax, [_gr_var_color]
189         mov     ah,al           ;duplicate in high byte for word access
190         and     ebx,ebx         ;left to right?
191         jns     DirSet          ;yes
192         sub     edi, edx        ;currently right to left, point to left end so we
193                                                         ; can go left to right (avoids unpleasantness with
194                                                         ; right to left REP STOSW)
195 DirSet:
196         mov     ecx, edx
197         inc     ecx             ;# of pixels to draw
198
199         shr     ecx, 1          ;# of words to draw
200         rep     stosw           ;do as many words as possible
201         adc     ecx, ecx
202         rep     stosb           ;do the odd byte, if there is one
203
204         jmp     Done
205
206 ; Special-case code for diagonal lines.
207
208 IsDiagonalLine:
209         mov     eax, [_gr_var_color]
210         add     ebx, ebp    ;advance distance from one pixel to next
211
212 DLoop:
213         mov     [edi],al
214         add     edi, ebx
215         dec     ecx
216         jns     DLoop
217         jmp     Done
218
219 NotVerticalLine:
220         mov     ebx,1               ;assume left to right, so [XAdvance] = 1
221                                                                 ;***leaves flags unchanged***
222         jns     LeftToRight         ;left to right, all set
223         neg     ebx                 ;right to left, so [XAdvance] = -1
224         neg     edx                 ;|XDelta|
225
226 LeftToRight:
227 ; Special-case horizontal lines.
228
229         and     ecx, ecx            ;YDelta == 0?
230         jz      IsHorizontalLine    ;yes
231
232 ; Special-case diagonal lines.
233         cmp     ecx, edx            ;YDelta == XDelta?
234         jz      IsDiagonalLine      ;yes
235
236 ; Determine whether the line is X or Y major, and handle accordingly.
237         cmp     edx, ecx
238         jae     XMajor
239         jmp     YMajor
240
241 ; X-major (more horizontal than vertical) line.
242
243 XMajor:
244                 and     ebx,ebx         ;left to right?
245                 jns     DFSet           ;yes, CLD is already set
246         std                     ;right to left, so draw backwards
247 DFSet:
248                 mov     eax,edx         ;XDelta
249                 sub     edx,edx         ;prepare for division
250                 div     ecx             ;EAX = XDelta/YDelta
251                                 ; (minimum # of pixels in a run in this line)
252                                                                 ;EDX = XDelta % YDelta
253                 mov     ebx,edx         ;error term adjust each time Y steps by 1;
254                 add     ebx,ebx         ; used to tell when one extra pixel should be
255                 mov     [AdjUp], ebx      ; drawn as part of a run, to account for
256                                 ; fractional steps along the X axis per
257                                 ; 1-pixel steps along Y
258                 mov     esi, ecx        ;error term adjust when the error term turns
259                 add     esi, esi        ; over, used to factor out the X step made at
260                 mov     [AdjDown], esi    ; that time
261
262 ; Initial error term; reflects an initial step of 0.5 along the Y axis.
263                 sub     edx, esi        ;(XDelta % YDelta) - (YDelta * 2)
264                                 ;DX = initial error term
265 ; The initial and last runs are partial, because Y advances only 0.5 for
266 ; these runs, rather than 1. Divide one full run, plus the initial pixel,
267 ; between the initial and last runs.
268                 mov     esi, ecx        ;SI = YDelta
269                 mov     ecx, eax        ;whole step (minimum run length)
270                 shr     ecx,1
271                 inc     ecx             ;initial pixel count = (whole step / 2) + 1;
272                                 ; (may be adjusted later). This is also the
273                 ; final run pixel count
274                 push    ecx             ;remember final run pixel count for later
275 ; If the basic run length is even and there's no fractional advance, we have
276 ; one pixel that could go to either the initial or last partial run, which
277 ; we'll arbitrarily allocate to the last run.
278 ; If there is an odd number of pixels per run, we have one pixel that can't
279 ; be allocated to either the initial or last partial run, so we'll add 0.5 to
280 ; the error term so this pixel will be handled by the normal full-run loop.
281                 add     edx,esi         ;assume odd length, add YDelta to error term
282                 ; (add 0.5 of a pixel to the error term)
283         test    al,1            ;is run length even?
284         jnz     XMajorAdjustDone ;no, already did work for odd case, all set
285                 sub     edx,esi         ;length is even, undo odd stuff we just did
286                 and     ebx,ebx         ;is the adjust up equal to 0?
287         jnz     XMajorAdjustDone ;no (don't need to check for odd length,
288                                                                 ; because of the above test)
289                 dec     ecx             ;both conditions met; make initial run 1
290                                 ; shorter
291
292 XMajorAdjustDone:
293                 mov     [WholeStep],eax   ;whole step (minimum run length)
294                 mov     eax, [_gr_var_color]       ;AL = drawing color
295 ; Draw the first, partial run of pixels.
296         rep     stosb           ;draw the final run
297                 add     edi,ebp ;advance along the minor axis (Y)
298 ; Draw all full runs.
299                 cmp     esi,1           ;are there more than 2 scans, so there are
300                                                                 ; some full runs? (SI = # scans - 1)
301         jna     XMajorDrawLast  ;no, no full runs
302                 dec     edx             ;adjust error term by -1 so we can use
303                                 ; carry test
304                 shr     esi,1           ;convert from scan to scan-pair count
305         jnc     XMajorFullRunsOddEntry  ;if there is an odd number of scans,
306                                         ; do the odd scan now
307 XMajorFullRunsLoop:
308                 mov     ecx, [WholeStep]  ;run is at least this long
309                 add     edx,ebx         ;advance the error term and add an extra
310         jnc     XMajorNoExtra   ; pixel if the error term so indicates
311                 inc     ecx             ;one extra pixel in run
312                 sub     edx,[AdjDown]     ;reset the error term
313 XMajorNoExtra:
314                 rep     stosb           ;draw this scan line's run
315                 add     edi,ebp ;advance along the minor axis (Y)
316 XMajorFullRunsOddEntry:         ;enter loop here if there is an odd number
317                                 ; of full runs
318                 mov     ecx,[WholeStep]   ;run is at least this long
319                 add     edx,ebx         ;advance the error term and add an extra
320         jnc     XMajorNoExtra2  ; pixel if the error term so indicates
321                 inc     ecx             ;one extra pixel in run
322                 sub     edx,[AdjDown]     ;reset the error term
323 XMajorNoExtra2:
324                 rep     stosb           ;draw this scan line's run
325                 add     edi,ebp ;advance along the minor axis (Y)
326
327                 dec     esi
328         jnz     XMajorFullRunsLoop
329 ; Draw the final run of pixels.
330 XMajorDrawLast:
331                 pop     ecx             ;get back the final run pixel length
332         rep     stosb           ;draw the final run
333
334         cld                     ;restore normal direction flag
335         jmp     Done
336 ; Y-major (more vertical than horizontal) line.
337 YMajor:
338                 mov     [XAdvance],ebx    ;remember which way X advances
339                 mov     eax,ecx         ;YDelta
340                 mov     ecx,edx         ;XDelta
341                 sub     edx,edx         ;prepare for division
342                 div     ecx             ;EAX = YDelta/XDelta
343                                 ; (minimum # of pixels in a run in this line)
344                                                                 ;EDX = YDelta % XDelta
345                 mov     ebx,edx         ;error term adjust each time X steps by 1;
346                 add     ebx,ebx         ; used to tell when one extra pixel should be
347                 mov     [AdjUp],ebx       ; drawn as part of a run, to account for
348                                 ; fractional steps along the Y axis per
349                                 ; 1-pixel steps along X
350                 mov     esi,ecx         ;error term adjust when the error term turns
351                 add     esi,esi         ; over, used to factor out the Y step made at
352                 mov     [AdjDown], esi    ; that time
353
354 ; Initial error term; reflects an initial step of 0.5 along the X axis.
355                 sub     edx,esi         ;(YDelta % XDelta) - (XDelta * 2)
356                                 ;DX = initial error term
357 ; The initial and last runs are partial, because X advances only 0.5 for
358 ; these runs, rather than 1. Divide one full run, plus the initial pixel,
359 ; between the initial and last runs.
360                 mov     esi,ecx         ;SI = XDelta
361                 mov     ecx,eax         ;whole step (minimum run length)
362                 shr     ecx,1
363                 inc     ecx             ;initial pixel count = (whole step / 2) + 1;
364                                 ; (may be adjusted later)
365                 push    ecx             ;remember final run pixel count for later
366
367 ; If the basic run length is even and there's no fractional advance, we have
368 ; one pixel that could go to either the initial or last partial run, which
369 ; we'll arbitrarily allocate to the last run.
370 ; If there is an odd number of pixels per run, we have one pixel that can't
371 ; be allocated to either the initial or last partial run, so we'll add 0.5 to
372 ; the error term so this pixel will be handled by the normal full-run loop.
373                 add     edx,esi         ;assume odd length, add XDelta to error term
374         test    al,1            ;is run length even?
375         jnz     YMajorAdjustDone ;no, already did work for odd case, all set
376                 sub     edx,esi         ;length is even, undo odd stuff we just did
377                 and     ebx,ebx         ;is the adjust up equal to 0?
378         jnz     YMajorAdjustDone ;no (don't need to check for odd length,
379                  ; because of the above test)
380                 dec     ecx             ;both conditions met; make initial run 1
381                                 ; shorter
382 YMajorAdjustDone:
383                 mov     [WholeStep],eax   ;whole step (minimum run length)
384                 mov     eax,[_gr_var_color]        ;AL = drawing color
385                 mov     ebx, [XAdvance]   ;which way X advances
386
387 ; Draw the first, partial run of pixels.
388 YMajorFirstLoop:
389                 mov     [edi],al        ;draw the pixel
390                 add     edi,ebp ;advance along the major axis (Y)
391                 dec     ecx
392         jnz     YMajorFirstLoop
393                 add     edi,ebx           ;advance along the minor axis (X)
394 ; Draw all full runs.
395                 cmp     esi,1            ;# of full runs. Are there more than 2
396                 ; columns, so there are some full runs?
397                 ; (SI = # columns - 1)
398         jna     YMajorDrawLast  ;no, no full runs
399                 dec     edx              ;adjust error term by -1 so we can use
400                                 ; carry test
401                 shr     esi,1            ;convert from column to column-pair count
402         jnc     YMajorFullRunsOddEntry  ;if there is an odd number of
403                                         ; columns, do the odd column now
404 YMajorFullRunsLoop:
405                 mov     ecx,[WholeStep]   ;run is at least this long
406                 add     edx,[AdjUp]       ;advance the error term and add an extra
407         jnc     YMajorNoExtra   ; pixel if the error term so indicates
408                 inc     ecx              ;one extra pixel in run
409                 sub     edx,[AdjDown]     ;reset the error term
410 YMajorNoExtra:
411                                 ;draw the run
412 YMajorRunLoop:
413                 mov     [edi],al        ;draw the pixel
414                 add     edi,ebp ;advance along the major axis (Y)
415                 dec     ecx
416         jnz     YMajorRunLoop
417                 add     edi,ebx         ;advance along the minor axis (X)
418 YMajorFullRunsOddEntry:         ;enter loop here if there is an odd number
419                                 ; of full runs
420                 mov     ecx,[WholeStep]   ;run is at least this long
421                 add     edx,[AdjUp]       ;advance the error term and add an extra
422         jnc     YMajorNoExtra2  ; pixel if the error term so indicates
423                 inc     ecx              ;one extra pixel in run
424                 sub     edx, [AdjDown]    ;reset the error term
425 YMajorNoExtra2:
426                                 ;draw the run
427 YMajorRunLoop2:
428                 mov     [edi],al         ;draw the pixel
429                 add     edi,ebp ;advance along the major axis (Y)
430                 dec     ecx
431         jnz     YMajorRunLoop2
432                 add     edi,ebx           ;advance along the minor axis (X)
433
434                 dec     esi
435         jnz     YMajorFullRunsLoop
436 ; Draw the final run of pixels.
437 YMajorDrawLast:
438                 pop     ecx              ;get back the final run pixel length
439 YMajorLastLoop:
440                 mov     [edi],al         ;draw the pixel
441                 add     edi,ebp ;advance along the major axis (Y)
442                 dec     ecx
443         jnz     YMajorLastLoop
444 Done:
445         pop ebp
446         pop edi
447         pop esi
448         pop ebx;restore C register variables
449     ret
450
451 global _gr_linear_stosd
452 global gr_linear_stosd
453
454 _gr_linear_stosd:
455 gr_linear_stosd:
456
457                         ; EAX -> Destination buffer
458                         ; EDX -> Byte to store
459                         ; EBX -> Number of bytes to move
460
461                         push    ebx
462                         push    edi
463                         mov     eax,[esp+12]
464                         mov     edx,[esp+16]
465                         mov     ebx,[esp+20]
466                         mov     edi, eax
467                         mov     dh, dl
468                         mov     ax, dx
469                         shl     eax, 16
470                         mov     ax, dx
471                         cld
472                         mov     ecx, ebx
473                         shr     ecx, 2
474                         rep     stosd
475                         mov     ecx, ebx
476                         and     ecx, 011b
477                         rep     stosb
478                         pop     edi
479                         pop     ebx
480                         ret