windows-nt/Source/XPSP1/NT/base/ntsetup/mpk/displib/vgaa.asm

816 lines
21 KiB
NASM
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
DOSSEG
.MODEL LARGE
VGA_BUFFER_SEG equ 0a000h
VGA_WIDTH_PIXELS equ 640
VGA_HEIGHT_SCAN_LINES equ 480
VGA_BYTES_PER_SCAN_LINE equ (VGA_WIDTH_PIXELS/8)
VGA_REGISTER equ 3ceh
VGAREG_SETRESET equ 0
VGAREG_ENABLESETRESET equ 1
VGAREG_READMAPSELECT equ 4
VGAREG_MODE equ 5
VGAREG_BITMASK equ 8
.CODE
extrn _malloc:far
.286
;++
;
; VOID
; _far
; VgaBitBlt(
; IN USHORT x,
; IN USHORT y,
; IN USHORT w,
; IN USHORT h,
; IN USHORT BytesPerRow,
; IN BOOL IsColor,
; IN FPBYTE PixelMap,
; IN FPVOID Data
; );
;
; Routine Description:
;
; Blits a rectangular block from memory to the screen.
; The block is interpreted as a monochrome bitmap.
;
; Arguments:
;
; x - gives the x position of the left edge of the target.
;
; y - gives the y position of the *bottom* edge of the target.
; The data is expected to be in standard Windows bottom-up
; format, and the bitmap grows upward from this line as
; it's transferred.
;
; w - gives the width in pixels of a row of the source bitmap.
;
; h - gives the number of lines in the source bitmap. The target
; is lines y through (y-h)+1 inclusive, in a bottom-up fashion.
;
; BytesPerRow - supplies the number of bytes in a row of data in the
; bitmap. Rows in a Windows bitmap are padded to a dword boundary.
;
; IsColor - if 0 the bitmap is interpreted as 1 bpp. If non-0 the bitmap
; is interpreted as 4 bpp.
;
; PixelMap - supplies an array of translations from pixel values in
; the source bitmap to values to be placed into video memory
; for that pixel. Each n-bit (1 for mono, 4 for color) value from
; the source bitmap is used as an index into this array.
; The 4-bit value looked up in the array is what is actually
; placed into video memory for the pixel. For mono this is a
; 2 byte table, for color it's 16.
;
; Data - supplies a pointer to the bitmap bits. The bits are expected
; to be in the standard Windows bitmap bottom-up arrangement.
; Each row of pixels is padded to a 4-byte boundary.
;
; Return Value:
;
; None.
;
;--
vbb_x equ word ptr [bp+6]
vbb_y equ word ptr [bp+8]
vbb_w equ word ptr [bp+10]
vbb_h equ [bp+12]
vbb_BytesPerRow equ word ptr [bp+14]
vbb_IsColor equ word ptr [bp+16]
vbb_PixelMap equ dword ptr [bp+18]
vbb_PixelMapl equ word ptr [bp+18]
vbb_PixelMaph equ word ptr [bp+20]
vbb_Datal equ word ptr [bp+22]
vbb_Datah equ word ptr [bp+24]
vbb_InitialShift equ byte ptr [bp-1]
vbb_DataByteMask equ byte ptr [bp-2]
vbb_ShiftAdjust equ byte ptr [bp-4]
vbb_PixelsRemain equ word ptr [bp-6]
vbb_BufferOffset equ word ptr [bp-8]
vbb_NextRowDelta equ word ptr [bp-10]
LocalPixelMap db 16 dup (?)
public _VgaBitBlt
_VgaBitBlt proc far
push bp
mov bp,sp
sub sp,10
push ds
push es
push si
push di
push bx
;
; Adjust for top-down vs bottom-up bitmap
;
mov vbb_NextRowDelta,VGA_BYTES_PER_SCAN_LINE
test byte ptr vbb_h[1],80h
jns @f
neg word ptr vbb_h ; make the height positive
neg vbb_NextRowDelta ; move backwards through buffer
;
; Set up read mode 0 and write mode 2.
;
@@: mov dx,VGA_REGISTER
mov al,VGAREG_MODE
mov ah,2 ; write mode 2, read mode 0
out dx,ax
;
; Set up bpp-dependent values
;
cmp vbb_IsColor,0
jz vbbmono
mov vbb_InitialShift,4
mov vbb_DataByteMask,0fh
mov vbb_ShiftAdjust,4 ; adjust shift count by 4 bits
mov cx,8 ; pixel map is 16 bytes = 8 words
jmp short vbbmovemap
vbbmono:
mov vbb_InitialShift,7
mov vbb_DataByteMask,1
mov vbb_ShiftAdjust,1 ; adjust shift count by 1 bit
mov cx,1 ; pixel map is 2 bytes = 1 words
vbbmovemap:
;
; Place the pixel map into memory addressable via cs.
; cx = count of words to move
;
push cs
pop es
lea di,LocalPixelMap ; es:di -> local copy of pixel map
mov si,vbb_PixelMapl
or si,vbb_PixelMaph
jnz vbbmovemap2 ; caller specified a pixel map
;
; No pixel map specified, build identity map
;
mov al,0
stosb
cmp vbb_IsColor,0
jne vbbcolor
mov al,0fh
stosb
jmp short vbbmovemap2
vbbcolor:
inc al
stosb
mov ax,0302h
stosw
mov ax,0504h
stosw
mov ax,0706h
stosw
mov ax,0908h
stosw
mov ax,0b0ah
stosw
mov ax,0d0ch
stosw
mov ax,0f0eh
stosw
jmp short vbbmovemap3
vbbmovemap2:
lds si,vbb_PixelMap ; ds:si -> pixel map
rep movsw
vbbmovemap3:
mov ax,vbb_Datah
mov ds,ax ; ds addresses bitmap data
mov ax,VGA_BUFFER_SEG
mov es,ax ; es addresses VGA video buffer
;
; Calculate address of first byte in video buffer we need to touch.
; Note that the y position is the *bottom* of the area to blit.
;
mov ax,VGA_BYTES_PER_SCAN_LINE
mul word ptr vbb_y ; ax = offset of first byte on line
mov vbb_BufferOffset,ax
mov ax,vbb_x
mov cl,3 ; divide by # bits in a byte (2^3)
shr ax,cl ; ax = offset of byte within line
add vbb_BufferOffset,ax ; form offset of byte in buffer
lea bx,LocalPixelMap ; cs:bx -> local copy of pixel map
mov dx,VGA_REGISTER ; mul above clobbered dx
vbbrow:
dec word ptr vbb_h ; done yet?
js vbbdone ; number went from 0 to -1, so we're done.
mov si,vbb_Datal ; ds:si -> start of row in bitmap data
mov ax,vbb_BytesPerRow
add vbb_Datal,ax ; point at next row for next iteration
mov di,vbb_BufferOffset
mov cx,vbb_NextRowDelta ; move up or down for next row
add vbb_BufferOffset,cx
mov cx,vbb_w
mov vbb_PixelsRemain,cx ; number of pixels remaining in row
mov cx,vbb_x
and cl,7 ; cl = bit number of first bit on line
sub cl,7
neg cl ; cl = shift count
mov ah,1
shl ah,cl ; ah = initial bit mask
mov al,es:[di] ; load latches for current byte
vbbfetch:
mov cl,vbb_InitialShift ; cl = shift count to extract data from bitmap
lodsb ; get next byte from bitmap
mov ch,al ; save it
vbbbyte:
;
; ah = current bit mask
;
mov al,VGAREG_BITMASK
out dx,ax
mov al,ch ; restore byte from bitmap
shr al,cl ; place value into low n bits
and al,vbb_DataByteMask ; al = value from bitmap
xlatb cs:[bx] ; al = pixel value to put into video memory
;
; Skip this pixel if the pixel value is greater than legal values
;
test al,0f0h
jnz vbbpix
;
; Stick this pixel into video memory.
; The bit mask is already set up to isloate the single pixel
; we want to modify.
;
mov es:[di],al
vbbpix:
;
; Adjust the bitmask to isolate the next pixel.
; If we've reached the end of the current byte then load the
; latches for the next one.
;
ror ah,1
jnc @f
inc di
@@: mov al,es:[di] ; update latches
;
; Now check termination conditions to see if we're done with
; the current row and if we're done with the current byte.
;
dec vbb_PixelsRemain ; done with this row yet?
jz vbbrow ; yes, start next row
sub cl,vbb_ShiftAdjust ; adjust shift count
jge vbbbyte ; get next pixel from byte we loaded earlier
jl vbbfetch ; get next byte from bitmap
vbbdone:
;
; Restore BIOS defaults
;
mov dx,VGA_REGISTER
mov al,VGAREG_MODE
mov ah,0 ; write mode 0, read mode 0
out dx,ax
pop bx
pop di
pop si
pop es
pop ds
leave
ret
_VgaBitBlt endp
;++
;
; VOID
; _far
; VgaClearRegion(
; IN USHORT x,
; IN USHORT y,
; IN USHORT w,
; IN USHORT h,
; IN BYTE PixelValue
; );
;
; Routine Description:
;
; Clears a given region on the vga screen to a given pixel value.
;
; Arguments:
;
; x,y,w,h - give the x and y of the upper left corner
; of the region to clear, and its width and height.
;
; PixelValue - supplies pixel value to clear to.
;
; Return Value:
;
; None.
;
;--
vcr_x equ word ptr [bp+6]
vcr_y equ word ptr [bp+8]
vcr_w equ [bp+10]
vcr_h equ word ptr [bp+12]
vcr_PixelValue equ byte ptr [bp+14]
vcr_FirstByte equ [bp+8]
vcr_startmask equ byte ptr [bp+6]
vcr_endmask equ byte ptr [bp+7]
vcr_fullbytes equ byte ptr [bp+15]
public _VgaClearRegion
_VgaClearRegion proc far
push bp
mov bp,sp
push di
;
; Calculate offset of first byte in first row we care about.
;
mov ax,VGA_BYTES_PER_SCAN_LINE
mul word ptr vcr_y ; ax = y position
mov vcr_FirstByte,ax ; store offset
;
; Now add in the byte offset of the byte containing the first pixel on
; each scan line.
;
mov ax,vcr_x ; ax = x position
mov cx,8 ; divide by # of bits in a byte
div cl ; ah = bit, al = byte
add vcr_FirstByte,al
adc byte ptr vcr_FirstByte[1],0 ; [bp+8] -> byte in scan line with first pixel
;
; Calculate bitmask for partial byte at start of line.
; The layout of the video buffer is such that the mask to address
; the 0th pixel is 80, to address the 1st pixel is 40, etc.
; Thus the bit within the byte is essentially the number of
; high bits in the 8 bit mask that need to be 0.
;
sub cl,ah ; cl = count of 1 bits, ch = 0
cmp cx,vcr_w ; too many for desired width?
pushf
mov ch,1
shl ch,cl
dec ch ; ch = bitmask, bottom cl bits are 1
popf
jbe vcr_storestart ; don't need to truncate width
sub cl,vcr_w ; cl = count of unwanted low bits
mov al,1
shl al,cl
dec al
not al
and ch,al
mov cl,vcr_w
vcr_storestart:
;
; cl = # pixels in partial byte
; ch = bitmask
;
mov vcr_startmask,ch ; save mask for start
xor ch,ch ; cx = pixels accounted for so far
mov ax,vcr_w
sub ax,cx ; ax = remaining width
jnz vcr_calcbytes
mov vcr_fullbytes,dl ; no full bytes
mov vcr_endmask,dl ; no partial at end of line
jmp short vcrdoit
vcr_calcbytes:
;
; ax = remaining width
;
mov cl,8
div cl ; al = full bytes, ah = partial
mov vcr_fullbytes,al
;
; Now calculate the mask for the partial byte at the end
; of each line. This will involve setting n of the high bits
; in a mask.
;
mov cl,8
sub cl,ah
mov al,1
shl al,cl
dec al
not al
mov vcr_endmask,al
vcrdoit:
mov dx,VGA_REGISTER
;
; The enable set/reset is set up so that the data that gets written
; into video memory comes from the set/reset register, not the cpu.
; Thus the content of ax is irrelevent when we store to video mem.
;
mov al,VGAREG_SETRESET ; set/reset register
mov ah,vcr_PixelValue ; ah = pixel value
out dx,ax ; load set/reset register
mov al,VGAREG_ENABLESETRESET ; enable set/reset register
mov ah,15 ; mask = write planes from set/reset
out dx,ax ; load enable set/reset register
mov ax,VGA_BUFFER_SEG
mov es,ax
mov cx,vcr_h ; get row count
nextrow:
push cx
mov di,vcr_FirstByte ; es:di -> first byte in target
add word ptr vcr_FirstByte,VGA_BYTES_PER_SCAN_LINE
;
; Do partial byte at relevent part of start of line
;
mov al,VGAREG_BITMASK
mov ah,vcr_startmask
out dx,ax
mov al,es:[di] ; load latches
stosb ; update video memory
;
; Do all full bytes on this line
;
mov al,VGAREG_BITMASK
mov ah,255
out dx,ax
mov cl,vcr_fullbytes
mov ch,0
rep stosb
;
; And finally the trail part
;
mov al,VGAREG_BITMASK
mov ah,vcr_endmask
out dx,ax
mov al,es:[di] ; load latches
stosb ; update video memory
pop cx ; restore row count
loop nextrow
;
; Restore default enable set/reset, bitmask
;
mov al,VGAREG_ENABLESETRESET
mov ah,0
mov dx,VGA_REGISTER
out dx,ax
mov al,VGAREG_BITMASK
mov ah,255
out dx,ax
pop di
leave
ret
_VgaClearRegion endp
;++
;
; VOID
; _far
; VgaClearScreen(
; IN BYTE PixelValue
; );
;
; Routine Description:
;
; Clears the 640x480 vga graphics screen.
;
; Arguments:
;
; PixelValue - supplies pixel value to clear to.
;
; Return Value:
;
; None.
;
;--
vcs_PixelValue equ [bp+6]
public _VgaClearScreen
_VgaClearScreen proc far
push bp
mov bp,sp
push vcs_PixelValue
push VGA_HEIGHT_SCAN_LINES
push VGA_WIDTH_PIXELS
push 0
push 0
call _VgaClearRegion
add sp,10
leave
ret
_VgaClearScreen endp
;++
;
; VOID
; _far
; VgaInit(
; VOID
; );
;
; Routine Description:
;
; Initializes display support by setting 640x480 vga graphics mode
; and clearing the screen.
;
; Arguments:
;
; None.
;
; Return Value:
;
; None.
;
;--
public _VgaInit
_VgaInit proc far
mov ax,12h
mov bx,0
int 10h
push 0 ; pixel value
call _VgaClearScreen
add sp,2
ret
_VgaInit endp
;++
;
; FPVOID
; _far
; VgaSaveBlock(
; IN USHORT x,
; IN USHORT y,
; IN USHORT w,
; IN USHORT h,
; IN FPUSHORT BytesPerRow
; );
;
; Routine Description:
;
; Grabs a block of memory from the screen and places it into a block
; of memory. The block can later be used with VgaBitBlt.
;
; This routine limits the size of the block to 32K. If the block
; would be larger than this, the routine fails.
;
; Arguments:
;
; x - supplies x coordinate of left edge of area to save.
; This MUST be an even multiple of 8. If it is not, the routine
; fails and returns NULL.
;
; y - supplies y coordinate of top of area to save.
;
; w - supplies width in pixels of area to save. This MUST be an even
; multiple of 8. If it is not, the routine fails and returns NULL.
;
; h - supplies height of area to save.
;
; BytesPerRow - receives number of bytes in a row of the saved block.
; This value is suitable for passing in as the BytesPerRow parameter
; to VgaBitBlt.
;
; Return Value:
;
; Pointer to bits, allocated via malloc(). Caller can free when finished.
; NULL if couldn't allocate enough memory for the saved block or invalid
; parameters were given.
;
;--
vsb_x equ word ptr [bp+6]
vsb_y equ word ptr [bp+8]
vsb_w equ word ptr [bp+10]
vsb_h equ word ptr [bp+12]
vsb_BytesPerRow equ dword ptr [bp+14]
vsb_BytesLeft equ word ptr [bp-2]
MakeAndSave2Pixels label near
;
; Take the 4 bytes from the bit planes and based on the top
; 2 bits of each, build 2 pixel values and save into the
; target bitmap. We take advantage of the fact that the sar
; instruction replicates the top bit as it shifts. This allows
; us to essentially shift 2 adjacent bits into nonadjacent
; bit positions.
;
mov dx,bx
mov ax,cx
sar dh,3 ; shift bit 7 to bit 7 and bit 6 to bit 3
sar dl,4 ; shift bit 7 to bit 6 and bit 6 to bit 2
sar ah,5 ; shift bit 7 to bit 5 and bit 6 to bit 1
sar al,6 ; shift bit 7 to bit 4 and bit 6 to bit 0
and dx,8844h ; isolate top 2 bits for each of two pixels
and ax,2211h ; and then isolate bottom 2 bits
or al,ah ; or in bit 1 for each pixel
or al,dl ; or in bit 2 for each pixel
or al,dh ; or in bit 3 for each pixel
stosb ; al has 2 pixel values, save in bitmap
retn
public _VgaSaveBlock
_VgaSaveBlock proc far
push bp
mov bp,sp
sub sp,2
push es
push si
push di
push bx
push ds
;
; Check conditions. Violation of them is catastrophic
; as the implementation simply can't handle it.
;
test vsb_x,7
jnz vsbinval
test vsb_w,7
jz vsbok
vsbinval:
xor ax,ax
mov dx,ax
jmp vsb_done
;
; Calculate the bytes per row. Rows are padded to a dword boundary.
; There are 4 bits per pixel and 8 bits per byte, so we can simply divide
; the width by 2 to calculate this value. Note that the result is
; always naturally aligned to a dword boundary.
;
vsbok:
mov ax,vsb_w
shr vsb_w,3 ; width is now expressed in bytes
shr ax,1
les di,vsb_BytesPerRow
mov es:[di],ax ; store in caller's variable
;
; Calculate the size of the buffer we need, which is simply
; height * bytes-per-row. Allocate a buffer and zero it out.
;
; NOTE: we have not disturbed ds yet. ds MUST be the caller's value
; before we attempt to call the CRTs!
;
; ax = bytes per row
;
mul vsb_h ; ax = buffer size needed
cmp dx,0 ; > 64K?
jnz vsbinval ; yes, too big
cmp ah,0 ; > 32K?
jl vsbinval ; yes, too big
push ax ; pass size to malloc
call _malloc
pop cx ; throw away size param off stack
mov bx,dx
or bx,ax
jz vsb_done ; dx:ax set for error return
mov es,dx
mov di,ax ; es:di -> bits
push ax ; save offset for later return
mov ax,VGA_BYTES_PER_SCAN_LINE
mul vsb_y ; ax -> first byte on first line
shr vsb_x,3 ; convert x coord to byte count
add vsb_x,ax ; vsb_x -> first byte to save
mov ax,VGA_BUFFER_SEG
mov ds,ax ; ds addresses vga video buffer
vsbrow:
mov si,vsb_x ; ds:si -> first byte in vga buf to save
add vsb_x,VGA_BYTES_PER_SCAN_LINE ; prepare for next iteration
mov dx,vsb_w
mov vsb_BytesLeft,dx
vsbbyte:
;
; Read the 4 planes at the current byte
;
mov dx,VGA_REGISTER
mov al,VGAREG_READMAPSELECT
mov ah,3
out dx,ax
mov bh,[si]
dec ah
out dx,ax
mov bl,[si]
dec ah
out dx,ax
mov ch,[si]
dec ah
out dx,ax
mov cl,[si]
inc si
;
; bh = byte from plane 3
; bl = byte from plane 2
; ch = byte from plane 1
; cl = byte from plane 0
;
call MakeAndSave2Pixels
shl bx,2
shl cx,2
call MakeAndSave2Pixels
shl bx,2
shl cx,2
call MakeAndSave2Pixels
shl bx,2
shl cx,2
call MakeAndSave2Pixels
;
; Do all bytes on this line.
;
dec vsb_BytesLeft
jnz vsbbyte
;
; Done with line, see if done.
;
dec vsb_h
jnz vsbrow
;
; Done, return buffer to caller.
;
mov dx,es
pop ax ; dx:ax -> bit buffer
vsb_done:
pop ds
pop bx
pop di
pop si
pop es
leave
ret
_VgaSaveBlock endp
END