📄 llcgasup.asm
字号:
cBegin
mov ah,[si] ;preload byte from array
inc si
jnz NWrOvfl1
call B$BumpDS ;move array pointer over segment boundary
NWrOvfl1:
ror ax,cl ;align to video
add bp,cx
sub bp,8 ;account for first partial byte
jbe NWrLast ;go if last byte
.erre ID_SSEQDS ;assumes ss = ds
call ss:[b$PutVector] ;put the byte
mov dh,0FFH ;mask for whole bytes in the middle
.erre ID_SSEQDS ;assumes ss = ds
mov bx,ss:[PutVectorM] ;preload the "full mask" put routine
jmp short NWrLoop2 ;go for more bytes
NWrLoop:
jmp bx ;vector to put the byte
;
;The following Put routines are identical to those documented above but
;are optimized for the middle loop where the mask is always 0FFH
;
;
; PutAndM:
;
PutAndM:
and es:[di],ah ;AND WITH SCREEN
jmp short PutEnd
;
; PutOrM:
;
PutOrM:
or es:[di],ah ;OR WITH SCREEN
jmp short PutEnd
;
; PutPresetM/PutPsetM
;
PutPresetM:
not ah ;NEGATE DATA FOR PRESET
PutPsetM:
mov es:[di],ah ;store data for PSET
jmp short PutEnd
;
; PutXorM
;
PutXorM:
xor es:[di],ah ;OR WITH SCREEN
PutEnd:
inc di ;bump to next screen byte
;
NWrLoop2:
rol ax,cl ;re-align to array
lodsb ;fill ax word with array bytes
or si,si ;did pointer overflow segment?
jz NWrOvfl3 ;go if so
NWrOvfl2:
xchg ah,al
ror ax,cl ;align to video
sub bp,8 ;8 bits done
ja NWrLoop ;go if bit count not exhausted
add bp,8 ;restore BP to #bits in last byte
cmp cx,bp ;did we use any of the second byte?
jb NWrLast ;go if so
or si,si ;at start of segment?
jnz NWrUnfl ;go if not
call B$DecDS ;backup to previous segment
NWrUnfl:
dec si ;move ptr back
NWrLast:
and dh,dl ;combine first|middle mask with end mask
.erre ID_SSEQDS ;assumes ss = ds
call ss:[b$PutVector] ;put the byte
cEnd
NWrOvfl3:
call B$BumpDS ;move array pointer over segment boundary
jmp short NWrOvfl2 ;back to loop
ASSUME DS:DGROUP
;***
; B$CgaNSetC
;
;Purpose:
; Set a horizontal line of pixels to the current attribute for CGA
; modes. The line starts at the current cursor position and moves right.
;
; Discussion of QCG tiling:
; Mode 6 tiling is relatively simple; to preserve the background
; pixels just AND the cursor mask with the byte at BKG_MASK[B$TILNDX].
; Mode 4/5 tiling is hairy because the fill mask is 8 pixels wide.
; This covers two bytes per row, which is wider than the current
; paint tiling code can do. Because QCG only lets the user supply
; the pixel on/off mask, as opposed to the actual pixel values, the
; tile_mask and b$AttrC values are the same for both bytes (actually,
; for all bytes, in CGA modes). Only the bkg_mask has to be 16-bits
; wide -- each row takes two consecutive bytes of bkg_mask.
;
;Entry:
; b$AddrC specifies start pixel
; b$PenC = cursor mask and attribute
; BX = number of pixels to set
;Exit:
; None
;Uses:
; per conv.
;Exceptions:
;******************************************************************************
cProc B$CgaNSetC,<PUBLIC,NEAR>,<ES,DI>
cBegin
les di,b$AddrC ;graphics cursor address
mov dx,b$PenC ;dl = cursor mask, dh = attribute
or dl,dl ;left aligned in byte?
js NSet2a ;go if so, skip single-bit start
NSet1:
xor ah,ah ;zero out new mask accumulator
mov cl,b$BitsPerPixel
NSet2:
or ah,dl ;include this pixel in mask
dec bx ;decrement pixel count
jz NSet4 ;treat as last byte if bit count exhausted
ror dl,cl ;move 1 pixel right
jnb NSet2 ;continue if not right-most bit
mov al,dh ;copy of attribute
xor al,es:[di] ;get bits that need to be changed
and al,ah ;mask in bits that need to be changed
xor es:[di],al ;update pixels
inc di ;bump cursor byte pointer
NSet2a:
mov ax,bx ;remaining bit count
mov cl,b$DivShift ;pixels/byte divisor shift
shr ax,cl ;compute full byte count
jz NSet3 ;go do remaining bits if no full bytes
xchg ax,cx ;byte count to cx
mov al,dh ;attribute byte
rep stosb ;block write full bytes
NSet3:
and bx,b$ModMask ;mask in remaining bit count
jz NSet5 ;no bits remaining - exit
jmp NSet1 ;go do remaining bits
NSet4: ;update last byte
xor dh,es:[di] ;get bits that need to be changed
and dh,ah ;mask in bits that need to be changed
xor es:[di],dh ;update pixels
NSet5:
cEnd
;***
; B$CgaSetTile
;
;Purpose:
; This routine stores the internal form of the current tile attribute.
; This routine is called each time a row is to be painted in CGA modes.
;Entry:
; BL = tile attribute
;Exit:
; _bAttrC set to tile attribute
;Uses:
; per conv.
;Exceptions:
;******************************************************************************
cProc B$CgaSetTile,<PUBLIC,NEAR>
cBegin
MOV b$AttrC,BL ;set attribute to the tile attribute
cEnd
;***
; ScanInit
;
;Purpose:
; This routine does some initialization for both ScanL and ScanR
; for CGA modes.
;Entry:
; None
;Exit:
; ES:DI = Video segment address (b$AddrC)
; CH = cursor mask (b$MaskC)
; DL = border attribute (b$PaintBorder)
; DH = paint attribute (b$AttrC)
; SI = already painted flag (0)
; BL = screen byte (read from ES:DI)
; CL = b$BitsPerPixel
;Uses:
; per conv.
;Exceptions:
;******************************************************************************
cProc ScanInit,<NEAR>
cBegin
les di,b$AddrC ;di=cursor offset, es=video seg
mov ch,b$MaskC ;ch=cursor mask
mov dl,b$PaintBorder ;dl=border attribute
mov dh,b$AttrC ;dh=paint attribute
mov cl,b$BitsPerPixel ;shift count
mov bl,es:[di] ;load the screen byte or word
xor si,si ;clear already-painted-flag
cEnd
;***
; Helper
;
;Purpose:
; This routine is used by the beginning and ending parts of
; SCANL and SCANR. It returns with ZF set if a border pixel was
; found, else it sets the already-painted-flag appropriately.
;Entry:
; BL = screen byte
; DL = border attribute
; CH = pixel mask
;Exit:
; If border found
; PSW.C set
; else
; AL = screen byte with non-masked pixels set to attribute
; SI = non-zero value
; BP (paint count) incremented
;Uses:
; per conv.
;Exceptions:
;******************************************************************************
cProc Helper,<NEAR>
cBegin
mov al,bl ;get screen byte
xor al,dl ;xor with border
and al,ch ;mask out unwanted bits
jz helpxt1 ;quit if border
mov al,bl ;get screen byte in [al]
xor al,dh ;xor it with paint attribute
and al,ch ;mask out other bits
or si,ax ;set already painted flag
inc bp ;increment paint count
helpxt1:
cEnd
;***
; B$CgaScanL
;
;Purpose:
; Scan left beginning with the pixel to the left of cursor,
; and paint pixels until:
; (1) the viewport edge is encounteered (edge painted)
; (2) a border pixel is encountered (border not painted)
;
;Entry:
; b$AddrC, b$MaskC = pixel to right of starting pixel
; b$PaintBorder = attribute of paint region border
; b$AttrC = attribute to paint
; B$LEOFST, B$VLMASK = left viewport edge
;Exit:
; BX = number of pixels scanned
; CL = 0 iff no pixels changed color
; b$OffC, b$MaskC = the last non-border pixel examined/painted
;Uses:
; per conv.
;Exceptions:
;******************************************************************************
cProc B$CgaScanL,<PUBLIC,NEAR>,<SI,DX,DI,BP,ES>
cBegin
call ScanInit ;init regs for scanning
mov bp,si ;clear paint count
; Prolog
cmp di,B$LEOFST ;cursor offset same as left edge offset ?
jz exit4 ;Brif so and do the ending
rol ch,cl ;move left one pixel
begin1:
JC MIDDLE ;go if byte aligned
call Helper ;check if this pixel is a border pixel
;else set the already painted flag
jz exit1 ;brif if border pixel found
rol ch,cl ;move left by one pixel
JMP SHORT begin1 ;else test the other pixels in this byte
middle:
; Middle paint loop
; Middle is used by all screen modes
middle0:
sub di,1 ;move left one byte
jb exit2 ;brif underflow
mov bl,es:[di] ;get the screen byte
cmp di,B$LEOFST ;are we at left edge ?
je exit3 ;brif so
mov al,bl ;screen byte in [al]
xor al,dl ;xor it with border
tstbdr1:
test al,ch ;border encountered ?
jz exit3 ;brif border
rol ch,cl ;prepare mask to test next pixel
jnb tstbdr1 ;test the next pixel
mov al,bl ;[al] = screen byte ****
xor al,dh ;xor screen byte with paint attribute
or si,ax ;set the already-painted-flag
add bp,b$PixelsPerByte ;increment paint count by # of
;pixels in a byte
JMP SHORT middle0 ;go do the rest
; Epilog
; branches here from the beginning part
exit1:
ror ch,cl ;back up so not on border
jnb final_exit ;brif no carry
JMP SHORT exit21
; branches here if [di] goes < 0
exit2:
mov ch,b$MaskLeft ;set mask with the leftmost
;bit/bits equal to 1
exit21:
inc di ;back up
JMP SHORT final_exit
; branches here if [di] is same as B$LEOFST or
; if a border pixel was found in the middle loop
exit3:
mov cl,b$BitsPerPixel
mov ch,b$MaskRight ;make the rightmost bit/bits equal to 1
exit31:
call Helper
jz exit1 ;brif border found
rol ch,cl ;move left by one pixel
jb exit41 ;this is a special case
cmp ch,B$VLMASK ;compare bit addresses
ja exit1 ;brif done
JMP SHORT exit31 ;else continue
; branches here if we start with cursor offset equal
; to B$LEOFST (right at the beginning)
exit4:
rol ch,cl ;move left by one pixel
; jnb exit31 ;continue if not the left most pixel
JB EXIT41 ;if leftmost pixel, then jump
CMP CH,B$VLMASK ;test if past the viewport on left
JNA EXIT31 ;if not, then jump
exit41:
ror ch,cl
; this is where NSetC gets called to do the actual painting
final_exit:
mov bx,bp ;paint count in [bx]
or bx,bx ;is paint count 0 ?
jz fexit1 ;branch around NSetC if so
mov b$OffC,di ;return cursor offset
mov b$MaskC,ch ;return cursor mask
call B$CgaNSetC ;paint them
fexit1:
mov bx,bp ;return paint count
mov cx,si ;return already-painted-flag
cEnd
;***
; B$CgaScanR
;
;Purpose:
; Starting with the current pixel, search right until:
; (1) a non-border pixel is found
; (2) [DX] pixels have been tested
; (3) the viewport edge is encountered
;
; If (2) or (3) terminated the scan, exit with:
; DX = remaining border bount = 0
;
; If (1) terminated the scan, scan and paint non-border pixels until:
; (1) the viewport edge is encountered (edge painted)
; (2) a border pixel is encountered (border not painted)
;
;Entry:
; DX = count of border pixels which may be skipped
; b$AddrC, b$MaskC = starting pixel
; b$PaintBorder = attribute of paint region border
; b$AttrC = attribute to paint
; B$REOFST, B$VRMASK = right viewport edge
;Exit:
; BX = number of pixels painted
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -