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