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.
14 ; Routines to access linear VGA memory
23 %define _gr_var_color gr_var_color
24 %define _gr_var_bitmap gr_var_bitmap
25 %define _gr_var_bwidth gr_var_bwidth
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
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.
51 global _gr_linear_line
58 push ebx ;preserve C register variables
72 mov ebp, [_gr_var_bwidth]
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.
80 xchg [YEnd], eax ;swap endpoints
88 ; Point EDI to the first pixel to draw.
90 mul edx ;[YStart] * ebp
93 add edi, [_gr_var_bitmap]
94 add edi, eax ;EDI = [YStart] * ebp + [XStart]
95 ; = offset of initial pixel
97 ; Figure out how far we're going vertically (guaranteed to be positive).
99 sub ecx, [YStart] ;ECX = YDelta
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.
107 jnz NotVerticalLine ;XDelta == 0 means vertical line
108 ;it is a vertical line
109 ;yes, special case vertical line
111 mov eax, [_gr_var_color]
119 ; Special-case code for horizontal lines.
122 mov eax, [_gr_var_color]
123 mov ah,al ;duplicate in high byte for word access
124 and ebx,ebx ;left to right?
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)
131 inc ecx ;# of pixels to draw
133 shr ecx, 1 ;# of words to draw
134 rep stosw ;do as many words as possible
136 rep stosb ;do the odd byte, if there is one
140 ; Special-case code for diagonal lines.
143 mov eax, [_gr_var_color]
144 add ebx, ebp ;advance distance from one pixel to next
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
161 ; Special-case horizontal lines.
163 and ecx, ecx ;YDelta == 0?
164 jz IsHorizontalLine ;yes
166 ; Special-case diagonal lines.
167 cmp ecx, edx ;YDelta == XDelta?
168 jz IsDiagonalLine ;yes
170 ; Determine whether the line is X or Y major, and handle accordingly.
175 ; X-major (more horizontal than vertical) line.
178 and ebx,ebx ;left to right?
179 jns DFSet ;yes, CLD is already set
180 std ;right to left, so draw backwards
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
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)
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
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
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
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
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
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
258 rep stosb ;draw this scan line's run
259 add edi,ebp ;advance along the minor axis (Y)
262 jnz XMajorFullRunsLoop
263 ; Draw the final run of pixels.
265 pop ecx ;get back the final run pixel length
266 rep stosb ;draw the final run
268 cld ;restore normal direction flag
270 ; Y-major (more vertical than horizontal) line.
272 mov [XAdvance],ebx ;remember which way X advances
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
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)
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
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
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
321 ; Draw the first, partial run of pixels.
323 mov [edi],al ;draw the pixel
324 add edi,ebp ;advance along the major axis (Y)
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
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
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
347 mov [edi],al ;draw the pixel
348 add edi,ebp ;advance along the major axis (Y)
351 add edi,ebx ;advance along the minor axis (X)
352 YMajorFullRunsOddEntry: ;enter loop here if there is an odd number
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
362 mov [edi],al ;draw the pixel
363 add edi,ebp ;advance along the major axis (Y)
366 add edi,ebx ;advance along the minor axis (X)
369 jnz YMajorFullRunsLoop
370 ; Draw the final run of pixels.
372 pop ecx ;get back the final run pixel length
374 mov [edi],al ;draw the pixel
375 add edi,ebp ;advance along the major axis (Y)
382 pop ebx;restore C register variables
385 global _gr_linear_stosd
386 global gr_linear_stosd
391 ; EAX -> Destination buffer
392 ; EDX -> Byte to store
393 ; EBX -> Number of bytes to move