816 lines
21 KiB
NASM
816 lines
21 KiB
NASM
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
|
|
|