neilobremski
Experienced Member
In this final, third part blog post I present to you finished 16-bit X86 assembler code for CGA mode 4 (320x200x4) written as a DEBUG script [SUP][1][/SUP]. This will give you a 556 byte routine starting at address 1300h.
Footnotes:
[SUP][1][/SUP] There are a few enhancements that I made using comments which allow me to insert and relocate things without screwing up the addressing. That said, the script is usable as is.
Code:
a 1300
; ----------------------------------------------------------------------------
; TRIFILL (AH,AL) - (BH,BL) - (DH,DL), CH :TRIFILL
;
; [params] [stepping] [drawing]
;
; / AH = X1 / StepA / XBeg
; AX /_ AL = Y1 / StepB / XEnd
;
; / BH = X2 / XDistA / (preserve)
; BX /_ BL = Y2 / YDistA / (preserve)
;
; / CH = Color / ErrA / PixAvail
; CX /_ CL = (ignore) / ErrB / Length
;
; / DH = X3 / XDistB / AdjSideA
; DX /_ DL = Y3 / YDistB / AdjSideB
;
; / 80 = (ignore) / Color / Color
; SI /_ 08 = (ignore) / Scanlines / Scanlines
;
; / 80 = (preserve) / XSideA / XSideA
; BP /_ 08 = (preserve) / XSideB / XSideB
;
; DI = DI = Pointer to beginning of current scanline
;
; IMPORTANT: All general registers but SP & BP will be modified by this routine!
;
PUSH BP ; 300 save stack frame
CLD ; 301 always write forward
XOR CL, CL ; 302
MOV SI, CX ; 304 save color to SI
XOR CH, CH ; 306
PUSH CX ; 308 end of triangle marker (0)
;
; SORT POINTS (TOP TO BOTTOM)
;
CMP AL, BL ; 309 compare Y1 to Y2 :TRI_SORT1
JBE 130E ; 30B jump next instruction >TRI_SORT2
XCHG AX, BX ; 30D swap P1 and P2
CMP AL, DL ; 30E compare Y1 to Y3 :TRI_SORT2
JBE 1313 ; 310 jump next instruction >TRI_SORT3
XCHG AX, DX ; 312 swap P1 and P3
CMP BL, DL ; 313 compare Y2 to Y3 :TRI_SORT3
JBE 1319 ; 315 jump next instruction >TRI_SORTDONE
XCHG BX, DX ; 317 swap P2 and P3
; :TRI_SORTDONE
; INITIAL SCANLINE (DI)
;
MOV BP, AX ; 319 save AX since we're going to mangle it :TRI_DI
XOR DI, DI ; 31B
XOR AH, AH ; 31D clear AH
TEST AL, 01 ; 31F is this an odd scanline?
JZ 1327 ; 321 jump next instruction >TRI_DI_OFFS
ADD DI, 2000; 323 add 0x2000 for odd scanline
AND AL, FE ; 327 clear bit 0 :TRI_DI_OFFS
SHL AX, 1 ; 329
SHL AX, 1 ; 32B
SHL AX, 1 ; 32D
ADD DI, AX ; 32F add *64 to DI
SHL AX, 1 ; 331
SHL AX, 1 ; 333
ADD DI, AX ; 335 add *16 to DI (*64 + *16 = *80 bytes per line)
MOV AX, BP ; 337 restore AX
;
; SLOPE 2to3 (SIDE A, BOTTOM HALF)
;
MOV CX, DX ; 339 :TRI_2TO3_PREP
SUB CL, BL ; 33B
JZ 137B ; 33D if (no bottom half) goto >TRI_1TO3_PREP
INC CL ; 33F YDist2to3 = Y3 - Y2 + 1
SUB CH, BH ; 341
JNC 1360 ; 343 >TRI_2TO3_RIGHT
NEG CH ; 345 :TRI_2TO3_LEFT
MOV BP, C529; 347 [SUB BP, AX]
MOV [1524], BP ; 34A $TRI_PRESTEP
MOV BP, F588; 34E [MOV CH, DH] XBeg = XSideA
PUSH BP ; 351
MOV BP, EE38; 352 [CMP DH, CH] if (XSideA <= XBeg)
PUSH BP ; 355
MOV BP, C6FE; 356 [INC DH]
PUSH BP ; 359
MOV BP, E628; 35A [SUB DH, AH]
PUSH BP ; 35D
JMP 1377 ; 35E >TRI_2TO3_DONE
;
MOV BP, C501; 360 [ADD BP, AX] :TRI_2TO3_RIGHT
MOV [1524], BP ; 363 $TRI_PRESTEP
MOV BP, F188; 367 [MOV CL, DH] XEnd = XSideA
PUSH BP ; 36A
MOV BP, F138; 36B [CMP CL, DH] if (XEnd <= XSideA)
PUSH BP ; 36E
MOV BP, CEFE; 36F [DEC DH]
PUSH BP ; 372
MOV BP, E600; 373 [ADD DH, AH]
PUSH BP ; 376
INC CH ; 377 XDist2to3 = X3 - X2 + 1 :TRI_2TO3_DONE
PUSH BX ; 379 save X2,.. for bottom half
PUSH CX ; 37A save XDist2to3,YDist2to3 for bottom half
;
; SLOPE 1to3 (SIDE B, LONG SIDE)
;
SUB DL, AL ; 37B :TRI_1TO3_PREP
INC DL ; 37D YDist1to3 = Y3 - Y1 + 1
SUB DH, AH ; 37F
JNC 13A3 ; 381 >TRI_1TO3_RIGHT
NEG DH ; 383 :TRI_1TO3_LEFT
MOV BP, C228; 385 [SUB DL, AL]
MOV [143C], BP ; 388 $TRI_CODE_ADDB
MOV BP, C2FE; 38C [INC DL]
MOV [1450], BP ; 38F $TRI_CODE_ADJB
MOV BP, EA38; 393 [CMP DL, CH] if (XSideB <= XBeg)
MOV [1452], BP ; 396 $TRI_CODE_CMPB
MOV BP, D588; 39A [MOV CH, DL] XBeg = XSideB
MOV [1456], BP ; 39D $TRI_CODE_USEB
JMP 13BF ; 3A1 >TRI_1TO3_DONE
;
MOV BP, C200; 3A3 [ADD DL, AL] :TRI_1TO3_RIGHT
MOV [143C], BP ; 3A6 $TRI_CODE_ADDB
MOV BP, CAFE; 3AA [DEC DL]
MOV [1450], BP ; 3AD $TRI_CODE_ADJB
MOV BP, D138; 3B1 [CMP CL, DL] if (XEnd <= XSideB)
MOV [1452], BP ; 3B4 $TRI_CODE_CMPB
MOV BP, D188; 3B8 [MOV CL, DL] XEnd = XSideB
MOV [1456], BP ; 3BB $TRI_CODE_USEB
INC DH ; 3BF XDist1to3 = X3 - X1 + 1 :TRI_1TO3_DONE
;
; SLOPE 1to2 (SIDE A, TOP HALF)
;
MOV CH, AH ; 3C1 :TRI_1TO2_PREP
MOV CL, AH ; 3C3
MOV BP, CX ; 3C5 XSideA,XSideB = X1,X1
SUB BL, AL ; 3C7
INC BL ; 3C9 YDist1to2 = Y2 - Y1 + 1
SUB BH, AH ; 3CB
JNC 13EF ; 3CD >TRI_1TO2_RIGHT
NEG BH ; 3CF :TRI_1TO2_LEFT
MOV CX, E628; 3D1 [SUB DH, AH]
MOV [143A], CX ; 3D4 $TRI_CODE_ADDA
MOV CX, C6FE; 3D8 [INC DH]
MOV [1444], CX ; 3DB $TRI_CODE_ADJA
MOV CX, EE38; 3DF [CMP DH, CH] if (XSideA <= XBeg)
MOV [1446], CX ; 3E2 $TRI_CODE_CMPA
MOV CX, F588; 3E6 [MOV CH, DH] XBeg = XSideA
MOV [144A], CX ; 3E9 $TRI_CODE_USEA
JMP 140B ; 3ED >TRI_1TO2_DONE
;
MOV CX, E600; 3EF [ADD DH, AH] :TRI_1TO2_RIGHT
MOV [143A], CX ; 3F2 $TRI_CODE_ADDA
MOV CX, CEFE; 3F6 [DEC DH]
MOV [1444], CX ; 3F9 $TRI_CODE_ADJA
MOV CX, F138; 3FD [CMP CL, DH] if (XEnd <= XSideA)
MOV [1446], CX ; 400 $TRI_CODE_CMPA
MOV CX, F188; 404 [MOV CL, DH] XEnd = XSideA
MOV [144A], CX ; 407 $TRI_CODE_USEA
INC BH ; 40B XDist1to2 = X2 - X1 + 1 :TRI_1TO2_DONE
;
; TRIFILL TOP HALF
;
MOV CL, BL ; 40D :TRIFILL_TOP
XOR CH, CH ; 40F ErrA = 0
OR SI, CX ; 411 Scanlines (LSB) = YDistA
XOR CL, CL ; 413 ErrB = 0
; --------------------------------------------------------------
; TRIFILL MAIN LOOP
;
XOR AX, AX ; 415 :TRIFILL_LOOP
;
; COMPUTE STEPS
;
MOV AL, CH ; 417
ADD AL, BH ; 419
ADC AH, AH ; 41B
DIV BL ; 41D ErrA /= YDistB
MOV CH, AH ; 41F ErrA = AH
XOR AH, AH ; 421
XCHG CL, AL ; 423 ErrB <swap> StepA
ADD AL, DH ; 425 ErrB += XDistB
ADC AH, AH ; 427
DIV DL ; 429 ErrB /= YDistB
XCHG AH, CL ; 42B ErrB <swap> StepA
;
PUSH CX ; 42D
PUSH DX ; 42E
PUSH DI ; 42F
;
; STEP SIDES (BP = new XSideA, XSideB)
;
MOV DX, BP ; 430
MOV CX, BP ; 432
CMP CH, CL ; 434 XBeg vs XEnd
JB 143A ; 436 >TRI_CODE_ADDA
XCHG CL, CH ; 438 XBeg,XEnd = Sort(XSideA, XSideB)
ADD DH, AH ; 43A :TRI_CODE_ADDA
ADD DL, AL ; 43C :TRI_CODE_ADDB
MOV BP, DX ; 43E XSideA += StepA, XSideB += StepB
;
; USE SIDE A TO EXPAND SEGMENT
;
OR AH, AH ; 440 :TRI_USE_SIDEA
JZ 144C ; 442 >TRI_USE_SIDEB
DEC DH ; 444 AdjSideA = XSideA - 1 :TRI_CODE_ADJA
CMP CL, DH ; 446 :TRI_CODE_CMPA
JA 144C ; 448 >TRI_USE_SIDEB
MOV CL, DH ; 44A :TRI_CODE_USEA
;
; USE SIDE B TO EXPAND SEGMENT
;
OR AL, AL ; 44C :TRI_USE_SIDEB
JZ 1458 ; 44E >TRI_DRAW_SEGLEN
DEC DL ; 450 AdjSideB = XSideB - 1 :TRI_CODE_ADJB
CMP AL, DL ; 452 :TRI_CODE_CMPB
JA 1458 ; 454 >TRI_DRAW_SEGLEN
MOV AL, DL ; 456 :TRI_CODE_USEB
;
; CALCULATE SEGMENT LENGTH
;
SUB CL, CH ; 458 :TRI_DRAW_SEGLEN
INC CL ; 45A Length = XEnd - XBeg + 1
;
; ADJUST DI TO FIRST PIXEL
;
MOV AL, CH ; 45C
XOR AH, AH ; 45E
SHR AL, 1 ; 460
SHR AL, 1 ; 462
ADD DI, AX ; 464 DI += XBeg / 4
MOV DH, FF ; 466 DH -> mask
AND CH, 3 ; 468
NEG CH ; 46B
ADD CH, 4 ; 46D PixAvail = 4 - (XBeg % 4)
CMP CH, CL ; 470
JB 148E ; 472 if (PixAvail > Length) goto >TRI_DRAW_BEGSEG
;
; DRAW SCAN LINE SEGMENT
;
SHL CL, 1 ; 474
SHR DH, CL ; 476
MOV CL, CH ; 478
SHL CL, 1 ; 47A
ROL DH, CL ; 47C
MOV AX, SI ; 47E
MOV AL, ES:[DI] ; 480
AND AL, DH ; 483
NOT DH ; 485
AND AH, DH ; 487
OR AL, AH ; 489
STOSB ; 48B store byte (DI is advanced but that is ignored)
JMP 14D1 ; 48C goto >TRI_NEXT_LINE
;
SUB CL, CH ; 48E Length -= PixAvail :TRI_DRAW_BEGSEG
SHL CH, 1 ; 490
XCHG CL, CH ; 492 save swap
SHL DH, CL ; 494
XCHG CL, CH ; 496 restore swap
MOV AX, SI ; 498
MOV AL, ES:[DI] ; 49A
AND AL, DH ; 49D
NOT DH ; 49F
AND AH, DH ; 4A1
OR AL, AH ; 4A3
STOSB ; 4A5
;
MOV DH, CL ; 4A6 :TRI_DRAW_MIDSEG
SHR CL, 1 ; 4A8
SHR CL, 1 ; 4AA
JZ 14B6 ; 4AC if (Length / 4 == 0) goto >TRI_DRAW_ENDSEG
MOV AX, SI ; 4AE
MOV AL, AH ; 4B0
XOR CH, CH ; 4B2
REP STOSB ; 4B4 let 'er rip!
;
AND DH, 03 ; 4B6 :TRI_DRAW_ENDSEG
JZ 14D1 ; 4B9 if (Length == 0) goto >TRI_NEXT_LINE
MOV CL, DH ; 4BB
MOV DH, FF ; 4BD
SHL CL, 1 ; 4BF
SHR DH, CL ; 4C1
MOV AX, SI ; 4C3
MOV AL, ES:[DI] ; 4C5
AND AL, DH ; 4C8
NOT DH ; 4CA
AND AH, DH ; 4CC
OR AL, AH ; 4CE
STOSB ; 4D0
;
POP DI ; 4D1 :TRI_NEXT_LINE
POP DX ; 4D2
POP CX ; 4D3
;
ADD DI, 2000; 4D4
CMP DI, 4000; 4D8
JAE 14E8 ; 4DC >TRI_ODD2EVEN
DEC SI ; 4DE
TEST SI, FF ; 4DF
JZ 14F6 ; 4E3 >TRIFILL_BOT
JMP 1415 ; 4E5 >TRIFILL_LOOP
SUB DI, 3FB0; 4E8 :TRI_ODD2EVEN
DEC SI ; 4EC
TEST SI, FF ; 4ED
JZ 14F6 ; 4F1 >TRIFILL_BOT
JMP 1415 ; 4F3 >TRIFILL_LOOP
; --------------------------------------------------------------
;
; TRIANGLE BOTTOM HALF
;
POP BX ; 4F6 pop XDist2to3,YDist2to3 :TRIFILL_BOT
OR BX, BX ; 4F7
JZ 1529 ; 4F9 if (end of bottom half) goto >TRIFILL_DONE
POP AX ; 4FB pop X2,..
XOR AL, AL ; 4FC
XCHG AX, BP ; 4FE
XOR AH, AH ; 4FF
OR BP, AX ; 501 XSideA,XSideB = X2,XSideB
MOV AL, BL ; 503
OR SI, AX ; 505
DEC SI ; 507 Scanlines (LSB) = YDist2to3 - 1
;
; REWRITE SIDE A CODE, BOTTOM HALF
;
POP AX ; 508
MOV [143A], AX ; 509 $TRI_CODE_ADDA
POP AX ; 50C
MOV [1444], AX ; 50D $TRI_CODE_ADJA
POP AX ; 510
MOV [1446], AX ; 511 $TRI_CODE_CMPA
POP AX ; 514
MOV [144A], AX ; 515 $TRI_CODE_USEA
;
; PRESTEP (first scanline is overlapped between halves)
;
XOR AX, AX ; 518
MOV AL, BH ; 51A
DIV BL ; 51C
MOV CH, AH ; 51E
MOV AH, AL ; 520
XOR AL, AL ; 522
ADD BP, AX ; 524 :TRI_PRESTEP
;
JMP 1415 ; 526 >TRIFILL_LOOP
;
POP BP ; 529 restore stack frame :TRIFILL_DONE
RET ; 52A
Footnotes:
[SUP][1][/SUP] There are a few enhancements that I made using comments which allow me to insert and relocate things without screwing up the addressing. That said, the script is usable as is.