show some missing wide 1's (bug #2269)
[btb/d2x.git] / 2d / linear.asm
1 ; $Id: linear.asm,v 1.5 2004-08-28 23:17:45 schaffner 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 ;
17
18 [BITS 32]
19
20 section .data
21                 ; Put data here
22 %ifdef __ELF__
23 %define _gr_var_color gr_var_color
24 %define _gr_var_bitmap gr_var_bitmap
25 %define _gr_var_bwidth gr_var_bwidth
26 %endif
27                 
28                 global _gr_var_color
29                 global _gr_var_bitmap
30                 global _gr_var_bwidth
31                 _gr_var_color   dd  0
32                 _gr_var_bitmap  dd  0
33                 _gr_var_bwidth  dd  0
34
35                 ; Local variables for gr_linear_line_
36                 AdjUp           dd  0   ;error term adjust up on each advance
37                 AdjDown         dd  0   ;error term adjust down when error term turns over
38                 WholeStep       dd  0   ;minimum run length
39                 XAdvance        dd  0   ;1 or -1, for direction in which X advances
40                 XStart          dd  0
41                 YStart          dd  0
42                 XEnd            dd  0
43                 YEnd            dd  0
44
45 section .text
46 ; Fast run length slice line drawing implementation for mode 0x13, the VGA's
47 ; 320x200 256-color mode.
48 ; Draws a line between the specified endpoints in color Color.
49
50
51 global _gr_linear_line
52 global gr_linear_line
53 _gr_linear_line:
54 gr_linear_line:
55
56         cld
57
58         push    ebx  ;preserve C register variables
59         push    esi
60         push    edi
61         push    ebp
62
63         mov     eax,[esp+20]
64         mov     edx,[esp+24]
65         mov     ebx,[esp+28]
66         mov     ecx,[esp+32]
67         mov     [XStart], eax
68         mov     [YStart], edx
69         mov     [XEnd], ebx
70         mov     [YEnd], ecx
71
72         mov     ebp, [_gr_var_bwidth]
73
74 ; We'll draw top to bottom, to reduce the number of cases we have to handle,
75 ; and to make lines between the same endpoints always draw the same pixels.
76
77         mov     eax,[YStart]
78         cmp     eax,[YEnd]
79         jle     LineIsTopToBottom
80         xchg    [YEnd], eax    ;swap endpoints
81         mov     [YStart], eax
82         mov     ebx, [XStart]
83         xchg    [XEnd], ebx
84         mov     [XStart], ebx
85
86 LineIsTopToBottom:
87
88 ; Point EDI to the first pixel to draw.
89         mov     edx,ebp
90         mul     edx             ;[YStart] * ebp
91         mov     esi, [XStart]
92         mov     edi, esi
93         add     edi, [_gr_var_bitmap]
94         add     edi, eax        ;EDI = [YStart] * ebp + [XStart]
95                                                         ; = offset of initial pixel
96
97 ; Figure out how far we're going vertically (guaranteed to be positive).
98         mov     ecx, [YEnd]
99         sub     ecx, [YStart]     ;ECX = YDelta
100
101 ; Figure out whether we're going left or right, and how far we're going
102 ; horizontally. In the process, special-case vertical lines, for speed and
103 ; to avoid nasty boundary conditions and division by 0.
104
105         mov     edx, [XEnd]
106         sub     edx, esi        ;XDelta
107         jnz     NotVerticalLine ;XDelta == 0 means vertical line
108                                                         ;it is a vertical line
109                                                         ;yes, special case vertical line
110
111         mov     eax, [_gr_var_color]
112 VLoop:
113         mov     [edi],al
114         add     edi,ebp
115         dec     ecx
116         jns     VLoop
117         jmp     Done
118
119 ; Special-case code for horizontal lines.
120
121 IsHorizontalLine:
122         mov     eax, [_gr_var_color]
123         mov     ah,al           ;duplicate in high byte for word access
124         and     ebx,ebx         ;left to right?
125         jns     DirSet          ;yes
126         sub     edi, edx        ;currently right to left, point to left end so we
127                                                         ; can go left to right (avoids unpleasantness with
128                                                         ; right to left REP STOSW)
129 DirSet:
130         mov     ecx, edx
131         inc     ecx             ;# of pixels to draw
132
133         shr     ecx, 1          ;# of words to draw
134         rep     stosw           ;do as many words as possible
135         adc     ecx, ecx
136         rep     stosb           ;do the odd byte, if there is one
137
138         jmp     Done
139
140 ; Special-case code for diagonal lines.
141
142 IsDiagonalLine:
143         mov     eax, [_gr_var_color]
144         add     ebx, ebp    ;advance distance from one pixel to next
145
146 DLoop:
147         mov     [edi],al
148         add     edi, ebx
149         dec     ecx
150         jns     DLoop
151         jmp     Done
152
153 NotVerticalLine:
154         mov     ebx,1               ;assume left to right, so [XAdvance] = 1
155                                                                 ;***leaves flags unchanged***
156         jns     LeftToRight         ;left to right, all set
157         neg     ebx                 ;right to left, so [XAdvance] = -1
158         neg     edx                 ;|XDelta|
159
160 LeftToRight:
161 ; Special-case horizontal lines.
162
163         and     ecx, ecx            ;YDelta == 0?
164         jz      IsHorizontalLine    ;yes
165
166 ; Special-case diagonal lines.
167         cmp     ecx, edx            ;YDelta == XDelta?
168         jz      IsDiagonalLine      ;yes
169
170 ; Determine whether the line is X or Y major, and handle accordingly.
171         cmp     edx, ecx
172         jae     XMajor
173         jmp     YMajor
174
175 ; X-major (more horizontal than vertical) line.
176
177 XMajor:
178                 and     ebx,ebx         ;left to right?
179                 jns     DFSet           ;yes, CLD is already set
180         std                     ;right to left, so draw backwards
181 DFSet:
182                 mov     eax,edx         ;XDelta
183                 sub     edx,edx         ;prepare for division
184                 div     ecx             ;EAX = XDelta/YDelta
185                                 ; (minimum # of pixels in a run in this line)
186                                                                 ;EDX = XDelta % YDelta
187                 mov     ebx,edx         ;error term adjust each time Y steps by 1;
188                 add     ebx,ebx         ; used to tell when one extra pixel should be
189                 mov     [AdjUp], ebx      ; drawn as part of a run, to account for
190                                 ; fractional steps along the X axis per
191                                 ; 1-pixel steps along Y
192                 mov     esi, ecx        ;error term adjust when the error term turns
193                 add     esi, esi        ; over, used to factor out the X step made at
194                 mov     [AdjDown], esi    ; that time
195
196 ; Initial error term; reflects an initial step of 0.5 along the Y axis.
197                 sub     edx, esi        ;(XDelta % YDelta) - (YDelta * 2)
198                                 ;DX = initial error term
199 ; The initial and last runs are partial, because Y advances only 0.5 for
200 ; these runs, rather than 1. Divide one full run, plus the initial pixel,
201 ; between the initial and last runs.
202                 mov     esi, ecx        ;SI = YDelta
203                 mov     ecx, eax        ;whole step (minimum run length)
204                 shr     ecx,1
205                 inc     ecx             ;initial pixel count = (whole step / 2) + 1;
206                                 ; (may be adjusted later). This is also the
207                 ; final run pixel count
208                 push    ecx             ;remember final run pixel count for later
209 ; If the basic run length is even and there's no fractional advance, we have
210 ; one pixel that could go to either the initial or last partial run, which
211 ; we'll arbitrarily allocate to the last run.
212 ; If there is an odd number of pixels per run, we have one pixel that can't
213 ; be allocated to either the initial or last partial run, so we'll add 0.5 to
214 ; the error term so this pixel will be handled by the normal full-run loop.
215                 add     edx,esi         ;assume odd length, add YDelta to error term
216                 ; (add 0.5 of a pixel to the error term)
217         test    al,1            ;is run length even?
218         jnz     XMajorAdjustDone ;no, already did work for odd case, all set
219                 sub     edx,esi         ;length is even, undo odd stuff we just did
220                 and     ebx,ebx         ;is the adjust up equal to 0?
221         jnz     XMajorAdjustDone ;no (don't need to check for odd length,
222                                                                 ; because of the above test)
223                 dec     ecx             ;both conditions met; make initial run 1
224                                 ; shorter
225
226 XMajorAdjustDone:
227                 mov     [WholeStep],eax   ;whole step (minimum run length)
228                 mov     eax, [_gr_var_color]       ;AL = drawing color
229 ; Draw the first, partial run of pixels.
230         rep     stosb           ;draw the final run
231                 add     edi,ebp ;advance along the minor axis (Y)
232 ; Draw all full runs.
233                 cmp     esi,1           ;are there more than 2 scans, so there are
234                                                                 ; some full runs? (SI = # scans - 1)
235         jna     XMajorDrawLast  ;no, no full runs
236                 dec     edx             ;adjust error term by -1 so we can use
237                                 ; carry test
238                 shr     esi,1           ;convert from scan to scan-pair count
239         jnc     XMajorFullRunsOddEntry  ;if there is an odd number of scans,
240                                         ; do the odd scan now
241 XMajorFullRunsLoop:
242                 mov     ecx, [WholeStep]  ;run is at least this long
243                 add     edx,ebx         ;advance the error term and add an extra
244         jnc     XMajorNoExtra   ; pixel if the error term so indicates
245                 inc     ecx             ;one extra pixel in run
246                 sub     edx,[AdjDown]     ;reset the error term
247 XMajorNoExtra:
248                 rep     stosb           ;draw this scan line's run
249                 add     edi,ebp ;advance along the minor axis (Y)
250 XMajorFullRunsOddEntry:         ;enter loop here if there is an odd number
251                                 ; of full runs
252                 mov     ecx,[WholeStep]   ;run is at least this long
253                 add     edx,ebx         ;advance the error term and add an extra
254         jnc     XMajorNoExtra2  ; pixel if the error term so indicates
255                 inc     ecx             ;one extra pixel in run
256                 sub     edx,[AdjDown]     ;reset the error term
257 XMajorNoExtra2:
258                 rep     stosb           ;draw this scan line's run
259                 add     edi,ebp ;advance along the minor axis (Y)
260
261                 dec     esi
262         jnz     XMajorFullRunsLoop
263 ; Draw the final run of pixels.
264 XMajorDrawLast:
265                 pop     ecx             ;get back the final run pixel length
266         rep     stosb           ;draw the final run
267
268         cld                     ;restore normal direction flag
269         jmp     Done
270 ; Y-major (more vertical than horizontal) line.
271 YMajor:
272                 mov     [XAdvance],ebx    ;remember which way X advances
273                 mov     eax,ecx         ;YDelta
274                 mov     ecx,edx         ;XDelta
275                 sub     edx,edx         ;prepare for division
276                 div     ecx             ;EAX = YDelta/XDelta
277                                 ; (minimum # of pixels in a run in this line)
278                                                                 ;EDX = YDelta % XDelta
279                 mov     ebx,edx         ;error term adjust each time X steps by 1;
280                 add     ebx,ebx         ; used to tell when one extra pixel should be
281                 mov     [AdjUp],ebx       ; drawn as part of a run, to account for
282                                 ; fractional steps along the Y axis per
283                                 ; 1-pixel steps along X
284                 mov     esi,ecx         ;error term adjust when the error term turns
285                 add     esi,esi         ; over, used to factor out the Y step made at
286                 mov     [AdjDown], esi    ; that time
287
288 ; Initial error term; reflects an initial step of 0.5 along the X axis.
289                 sub     edx,esi         ;(YDelta % XDelta) - (XDelta * 2)
290                                 ;DX = initial error term
291 ; The initial and last runs are partial, because X advances only 0.5 for
292 ; these runs, rather than 1. Divide one full run, plus the initial pixel,
293 ; between the initial and last runs.
294                 mov     esi,ecx         ;SI = XDelta
295                 mov     ecx,eax         ;whole step (minimum run length)
296                 shr     ecx,1
297                 inc     ecx             ;initial pixel count = (whole step / 2) + 1;
298                                 ; (may be adjusted later)
299                 push    ecx             ;remember final run pixel count for later
300
301 ; If the basic run length is even and there's no fractional advance, we have
302 ; one pixel that could go to either the initial or last partial run, which
303 ; we'll arbitrarily allocate to the last run.
304 ; If there is an odd number of pixels per run, we have one pixel that can't
305 ; be allocated to either the initial or last partial run, so we'll add 0.5 to
306 ; the error term so this pixel will be handled by the normal full-run loop.
307                 add     edx,esi         ;assume odd length, add XDelta to error term
308         test    al,1            ;is run length even?
309         jnz     YMajorAdjustDone ;no, already did work for odd case, all set
310                 sub     edx,esi         ;length is even, undo odd stuff we just did
311                 and     ebx,ebx         ;is the adjust up equal to 0?
312         jnz     YMajorAdjustDone ;no (don't need to check for odd length,
313                  ; because of the above test)
314                 dec     ecx             ;both conditions met; make initial run 1
315                                 ; shorter
316 YMajorAdjustDone:
317                 mov     [WholeStep],eax   ;whole step (minimum run length)
318                 mov     eax,[_gr_var_color]        ;AL = drawing color
319                 mov     ebx, [XAdvance]   ;which way X advances
320
321 ; Draw the first, partial run of pixels.
322 YMajorFirstLoop:
323                 mov     [edi],al        ;draw the pixel
324                 add     edi,ebp ;advance along the major axis (Y)
325                 dec     ecx
326         jnz     YMajorFirstLoop
327                 add     edi,ebx           ;advance along the minor axis (X)
328 ; Draw all full runs.
329                 cmp     esi,1            ;# of full runs. Are there more than 2
330                 ; columns, so there are some full runs?
331                 ; (SI = # columns - 1)
332         jna     YMajorDrawLast  ;no, no full runs
333                 dec     edx              ;adjust error term by -1 so we can use
334                                 ; carry test
335                 shr     esi,1            ;convert from column to column-pair count
336         jnc     YMajorFullRunsOddEntry  ;if there is an odd number of
337                                         ; columns, do the odd column now
338 YMajorFullRunsLoop:
339                 mov     ecx,[WholeStep]   ;run is at least this long
340                 add     edx,[AdjUp]       ;advance the error term and add an extra
341         jnc     YMajorNoExtra   ; pixel if the error term so indicates
342                 inc     ecx              ;one extra pixel in run
343                 sub     edx,[AdjDown]     ;reset the error term
344 YMajorNoExtra:
345                                 ;draw the run
346 YMajorRunLoop:
347                 mov     [edi],al        ;draw the pixel
348                 add     edi,ebp ;advance along the major axis (Y)
349                 dec     ecx
350         jnz     YMajorRunLoop
351                 add     edi,ebx         ;advance along the minor axis (X)
352 YMajorFullRunsOddEntry:         ;enter loop here if there is an odd number
353                                 ; of full runs
354                 mov     ecx,[WholeStep]   ;run is at least this long
355                 add     edx,[AdjUp]       ;advance the error term and add an extra
356         jnc     YMajorNoExtra2  ; pixel if the error term so indicates
357                 inc     ecx              ;one extra pixel in run
358                 sub     edx, [AdjDown]    ;reset the error term
359 YMajorNoExtra2:
360                                 ;draw the run
361 YMajorRunLoop2:
362                 mov     [edi],al         ;draw the pixel
363                 add     edi,ebp ;advance along the major axis (Y)
364                 dec     ecx
365         jnz     YMajorRunLoop2
366                 add     edi,ebx           ;advance along the minor axis (X)
367
368                 dec     esi
369         jnz     YMajorFullRunsLoop
370 ; Draw the final run of pixels.
371 YMajorDrawLast:
372                 pop     ecx              ;get back the final run pixel length
373 YMajorLastLoop:
374                 mov     [edi],al         ;draw the pixel
375                 add     edi,ebp ;advance along the major axis (Y)
376                 dec     ecx
377         jnz     YMajorLastLoop
378 Done:
379         pop ebp
380         pop edi
381         pop esi
382         pop ebx;restore C register variables
383     ret
384
385 global _gr_linear_stosd
386 global gr_linear_stosd
387
388 _gr_linear_stosd:
389 gr_linear_stosd:
390
391                         ; EAX -> Destination buffer
392                         ; EDX -> Byte to store
393                         ; EBX -> Number of bytes to move
394
395                         push    ebx
396                         push    edi
397                         mov     eax,[esp+12]
398                         mov     edx,[esp+16]
399                         mov     ebx,[esp+20]
400                         mov     edi, eax
401                         mov     dh, dl
402                         mov     ax, dx
403                         shl     eax, 16
404                         mov     ax, dx
405                         cld
406                         mov     ecx, ebx
407                         shr     ecx, 2
408                         rep     stosd
409                         mov     ecx, ebx
410                         and     ecx, 011b
411                         rep     stosb
412                         pop     edi
413                         pop     ebx
414                         ret