Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

815 lines
21 KiB

  1. DOSSEG
  2. .MODEL LARGE
  3. VGA_BUFFER_SEG equ 0a000h
  4. VGA_WIDTH_PIXELS equ 640
  5. VGA_HEIGHT_SCAN_LINES equ 480
  6. VGA_BYTES_PER_SCAN_LINE equ (VGA_WIDTH_PIXELS/8)
  7. VGA_REGISTER equ 3ceh
  8. VGAREG_SETRESET equ 0
  9. VGAREG_ENABLESETRESET equ 1
  10. VGAREG_READMAPSELECT equ 4
  11. VGAREG_MODE equ 5
  12. VGAREG_BITMASK equ 8
  13. .CODE
  14. extrn _malloc:far
  15. .286
  16. ;++
  17. ;
  18. ; VOID
  19. ; _far
  20. ; VgaBitBlt(
  21. ; IN USHORT x,
  22. ; IN USHORT y,
  23. ; IN USHORT w,
  24. ; IN USHORT h,
  25. ; IN USHORT BytesPerRow,
  26. ; IN BOOL IsColor,
  27. ; IN FPBYTE PixelMap,
  28. ; IN FPVOID Data
  29. ; );
  30. ;
  31. ; Routine Description:
  32. ;
  33. ; Blits a rectangular block from memory to the screen.
  34. ; The block is interpreted as a monochrome bitmap.
  35. ;
  36. ; Arguments:
  37. ;
  38. ; x - gives the x position of the left edge of the target.
  39. ;
  40. ; y - gives the y position of the *bottom* edge of the target.
  41. ; The data is expected to be in standard Windows bottom-up
  42. ; format, and the bitmap grows upward from this line as
  43. ; it's transferred.
  44. ;
  45. ; w - gives the width in pixels of a row of the source bitmap.
  46. ;
  47. ; h - gives the number of lines in the source bitmap. The target
  48. ; is lines y through (y-h)+1 inclusive, in a bottom-up fashion.
  49. ;
  50. ; BytesPerRow - supplies the number of bytes in a row of data in the
  51. ; bitmap. Rows in a Windows bitmap are padded to a dword boundary.
  52. ;
  53. ; IsColor - if 0 the bitmap is interpreted as 1 bpp. If non-0 the bitmap
  54. ; is interpreted as 4 bpp.
  55. ;
  56. ; PixelMap - supplies an array of translations from pixel values in
  57. ; the source bitmap to values to be placed into video memory
  58. ; for that pixel. Each n-bit (1 for mono, 4 for color) value from
  59. ; the source bitmap is used as an index into this array.
  60. ; The 4-bit value looked up in the array is what is actually
  61. ; placed into video memory for the pixel. For mono this is a
  62. ; 2 byte table, for color it's 16.
  63. ;
  64. ; Data - supplies a pointer to the bitmap bits. The bits are expected
  65. ; to be in the standard Windows bitmap bottom-up arrangement.
  66. ; Each row of pixels is padded to a 4-byte boundary.
  67. ;
  68. ; Return Value:
  69. ;
  70. ; None.
  71. ;
  72. ;--
  73. vbb_x equ word ptr [bp+6]
  74. vbb_y equ word ptr [bp+8]
  75. vbb_w equ word ptr [bp+10]
  76. vbb_h equ [bp+12]
  77. vbb_BytesPerRow equ word ptr [bp+14]
  78. vbb_IsColor equ word ptr [bp+16]
  79. vbb_PixelMap equ dword ptr [bp+18]
  80. vbb_PixelMapl equ word ptr [bp+18]
  81. vbb_PixelMaph equ word ptr [bp+20]
  82. vbb_Datal equ word ptr [bp+22]
  83. vbb_Datah equ word ptr [bp+24]
  84. vbb_InitialShift equ byte ptr [bp-1]
  85. vbb_DataByteMask equ byte ptr [bp-2]
  86. vbb_ShiftAdjust equ byte ptr [bp-4]
  87. vbb_PixelsRemain equ word ptr [bp-6]
  88. vbb_BufferOffset equ word ptr [bp-8]
  89. vbb_NextRowDelta equ word ptr [bp-10]
  90. LocalPixelMap db 16 dup (?)
  91. public _VgaBitBlt
  92. _VgaBitBlt proc far
  93. push bp
  94. mov bp,sp
  95. sub sp,10
  96. push ds
  97. push es
  98. push si
  99. push di
  100. push bx
  101. ;
  102. ; Adjust for top-down vs bottom-up bitmap
  103. ;
  104. mov vbb_NextRowDelta,VGA_BYTES_PER_SCAN_LINE
  105. test byte ptr vbb_h[1],80h
  106. jns @f
  107. neg word ptr vbb_h ; make the height positive
  108. neg vbb_NextRowDelta ; move backwards through buffer
  109. ;
  110. ; Set up read mode 0 and write mode 2.
  111. ;
  112. @@: mov dx,VGA_REGISTER
  113. mov al,VGAREG_MODE
  114. mov ah,2 ; write mode 2, read mode 0
  115. out dx,ax
  116. ;
  117. ; Set up bpp-dependent values
  118. ;
  119. cmp vbb_IsColor,0
  120. jz vbbmono
  121. mov vbb_InitialShift,4
  122. mov vbb_DataByteMask,0fh
  123. mov vbb_ShiftAdjust,4 ; adjust shift count by 4 bits
  124. mov cx,8 ; pixel map is 16 bytes = 8 words
  125. jmp short vbbmovemap
  126. vbbmono:
  127. mov vbb_InitialShift,7
  128. mov vbb_DataByteMask,1
  129. mov vbb_ShiftAdjust,1 ; adjust shift count by 1 bit
  130. mov cx,1 ; pixel map is 2 bytes = 1 words
  131. vbbmovemap:
  132. ;
  133. ; Place the pixel map into memory addressable via cs.
  134. ; cx = count of words to move
  135. ;
  136. push cs
  137. pop es
  138. lea di,LocalPixelMap ; es:di -> local copy of pixel map
  139. mov si,vbb_PixelMapl
  140. or si,vbb_PixelMaph
  141. jnz vbbmovemap2 ; caller specified a pixel map
  142. ;
  143. ; No pixel map specified, build identity map
  144. ;
  145. mov al,0
  146. stosb
  147. cmp vbb_IsColor,0
  148. jne vbbcolor
  149. mov al,0fh
  150. stosb
  151. jmp short vbbmovemap2
  152. vbbcolor:
  153. inc al
  154. stosb
  155. mov ax,0302h
  156. stosw
  157. mov ax,0504h
  158. stosw
  159. mov ax,0706h
  160. stosw
  161. mov ax,0908h
  162. stosw
  163. mov ax,0b0ah
  164. stosw
  165. mov ax,0d0ch
  166. stosw
  167. mov ax,0f0eh
  168. stosw
  169. jmp short vbbmovemap3
  170. vbbmovemap2:
  171. lds si,vbb_PixelMap ; ds:si -> pixel map
  172. rep movsw
  173. vbbmovemap3:
  174. mov ax,vbb_Datah
  175. mov ds,ax ; ds addresses bitmap data
  176. mov ax,VGA_BUFFER_SEG
  177. mov es,ax ; es addresses VGA video buffer
  178. ;
  179. ; Calculate address of first byte in video buffer we need to touch.
  180. ; Note that the y position is the *bottom* of the area to blit.
  181. ;
  182. mov ax,VGA_BYTES_PER_SCAN_LINE
  183. mul word ptr vbb_y ; ax = offset of first byte on line
  184. mov vbb_BufferOffset,ax
  185. mov ax,vbb_x
  186. mov cl,3 ; divide by # bits in a byte (2^3)
  187. shr ax,cl ; ax = offset of byte within line
  188. add vbb_BufferOffset,ax ; form offset of byte in buffer
  189. lea bx,LocalPixelMap ; cs:bx -> local copy of pixel map
  190. mov dx,VGA_REGISTER ; mul above clobbered dx
  191. vbbrow:
  192. dec word ptr vbb_h ; done yet?
  193. js vbbdone ; number went from 0 to -1, so we're done.
  194. mov si,vbb_Datal ; ds:si -> start of row in bitmap data
  195. mov ax,vbb_BytesPerRow
  196. add vbb_Datal,ax ; point at next row for next iteration
  197. mov di,vbb_BufferOffset
  198. mov cx,vbb_NextRowDelta ; move up or down for next row
  199. add vbb_BufferOffset,cx
  200. mov cx,vbb_w
  201. mov vbb_PixelsRemain,cx ; number of pixels remaining in row
  202. mov cx,vbb_x
  203. and cl,7 ; cl = bit number of first bit on line
  204. sub cl,7
  205. neg cl ; cl = shift count
  206. mov ah,1
  207. shl ah,cl ; ah = initial bit mask
  208. mov al,es:[di] ; load latches for current byte
  209. vbbfetch:
  210. mov cl,vbb_InitialShift ; cl = shift count to extract data from bitmap
  211. lodsb ; get next byte from bitmap
  212. mov ch,al ; save it
  213. vbbbyte:
  214. ;
  215. ; ah = current bit mask
  216. ;
  217. mov al,VGAREG_BITMASK
  218. out dx,ax
  219. mov al,ch ; restore byte from bitmap
  220. shr al,cl ; place value into low n bits
  221. and al,vbb_DataByteMask ; al = value from bitmap
  222. xlatb cs:[bx] ; al = pixel value to put into video memory
  223. ;
  224. ; Skip this pixel if the pixel value is greater than legal values
  225. ;
  226. test al,0f0h
  227. jnz vbbpix
  228. ;
  229. ; Stick this pixel into video memory.
  230. ; The bit mask is already set up to isloate the single pixel
  231. ; we want to modify.
  232. ;
  233. mov es:[di],al
  234. vbbpix:
  235. ;
  236. ; Adjust the bitmask to isolate the next pixel.
  237. ; If we've reached the end of the current byte then load the
  238. ; latches for the next one.
  239. ;
  240. ror ah,1
  241. jnc @f
  242. inc di
  243. @@: mov al,es:[di] ; update latches
  244. ;
  245. ; Now check termination conditions to see if we're done with
  246. ; the current row and if we're done with the current byte.
  247. ;
  248. dec vbb_PixelsRemain ; done with this row yet?
  249. jz vbbrow ; yes, start next row
  250. sub cl,vbb_ShiftAdjust ; adjust shift count
  251. jge vbbbyte ; get next pixel from byte we loaded earlier
  252. jl vbbfetch ; get next byte from bitmap
  253. vbbdone:
  254. ;
  255. ; Restore BIOS defaults
  256. ;
  257. mov dx,VGA_REGISTER
  258. mov al,VGAREG_MODE
  259. mov ah,0 ; write mode 0, read mode 0
  260. out dx,ax
  261. pop bx
  262. pop di
  263. pop si
  264. pop es
  265. pop ds
  266. leave
  267. ret
  268. _VgaBitBlt endp
  269. ;++
  270. ;
  271. ; VOID
  272. ; _far
  273. ; VgaClearRegion(
  274. ; IN USHORT x,
  275. ; IN USHORT y,
  276. ; IN USHORT w,
  277. ; IN USHORT h,
  278. ; IN BYTE PixelValue
  279. ; );
  280. ;
  281. ; Routine Description:
  282. ;
  283. ; Clears a given region on the vga screen to a given pixel value.
  284. ;
  285. ; Arguments:
  286. ;
  287. ; x,y,w,h - give the x and y of the upper left corner
  288. ; of the region to clear, and its width and height.
  289. ;
  290. ; PixelValue - supplies pixel value to clear to.
  291. ;
  292. ; Return Value:
  293. ;
  294. ; None.
  295. ;
  296. ;--
  297. vcr_x equ word ptr [bp+6]
  298. vcr_y equ word ptr [bp+8]
  299. vcr_w equ [bp+10]
  300. vcr_h equ word ptr [bp+12]
  301. vcr_PixelValue equ byte ptr [bp+14]
  302. vcr_FirstByte equ [bp+8]
  303. vcr_startmask equ byte ptr [bp+6]
  304. vcr_endmask equ byte ptr [bp+7]
  305. vcr_fullbytes equ byte ptr [bp+15]
  306. public _VgaClearRegion
  307. _VgaClearRegion proc far
  308. push bp
  309. mov bp,sp
  310. push di
  311. ;
  312. ; Calculate offset of first byte in first row we care about.
  313. ;
  314. mov ax,VGA_BYTES_PER_SCAN_LINE
  315. mul word ptr vcr_y ; ax = y position
  316. mov vcr_FirstByte,ax ; store offset
  317. ;
  318. ; Now add in the byte offset of the byte containing the first pixel on
  319. ; each scan line.
  320. ;
  321. mov ax,vcr_x ; ax = x position
  322. mov cx,8 ; divide by # of bits in a byte
  323. div cl ; ah = bit, al = byte
  324. add vcr_FirstByte,al
  325. adc byte ptr vcr_FirstByte[1],0 ; [bp+8] -> byte in scan line with first pixel
  326. ;
  327. ; Calculate bitmask for partial byte at start of line.
  328. ; The layout of the video buffer is such that the mask to address
  329. ; the 0th pixel is 80, to address the 1st pixel is 40, etc.
  330. ; Thus the bit within the byte is essentially the number of
  331. ; high bits in the 8 bit mask that need to be 0.
  332. ;
  333. sub cl,ah ; cl = count of 1 bits, ch = 0
  334. cmp cx,vcr_w ; too many for desired width?
  335. pushf
  336. mov ch,1
  337. shl ch,cl
  338. dec ch ; ch = bitmask, bottom cl bits are 1
  339. popf
  340. jbe vcr_storestart ; don't need to truncate width
  341. sub cl,vcr_w ; cl = count of unwanted low bits
  342. mov al,1
  343. shl al,cl
  344. dec al
  345. not al
  346. and ch,al
  347. mov cl,vcr_w
  348. vcr_storestart:
  349. ;
  350. ; cl = # pixels in partial byte
  351. ; ch = bitmask
  352. ;
  353. mov vcr_startmask,ch ; save mask for start
  354. xor ch,ch ; cx = pixels accounted for so far
  355. mov ax,vcr_w
  356. sub ax,cx ; ax = remaining width
  357. jnz vcr_calcbytes
  358. mov vcr_fullbytes,dl ; no full bytes
  359. mov vcr_endmask,dl ; no partial at end of line
  360. jmp short vcrdoit
  361. vcr_calcbytes:
  362. ;
  363. ; ax = remaining width
  364. ;
  365. mov cl,8
  366. div cl ; al = full bytes, ah = partial
  367. mov vcr_fullbytes,al
  368. ;
  369. ; Now calculate the mask for the partial byte at the end
  370. ; of each line. This will involve setting n of the high bits
  371. ; in a mask.
  372. ;
  373. mov cl,8
  374. sub cl,ah
  375. mov al,1
  376. shl al,cl
  377. dec al
  378. not al
  379. mov vcr_endmask,al
  380. vcrdoit:
  381. mov dx,VGA_REGISTER
  382. ;
  383. ; The enable set/reset is set up so that the data that gets written
  384. ; into video memory comes from the set/reset register, not the cpu.
  385. ; Thus the content of ax is irrelevent when we store to video mem.
  386. ;
  387. mov al,VGAREG_SETRESET ; set/reset register
  388. mov ah,vcr_PixelValue ; ah = pixel value
  389. out dx,ax ; load set/reset register
  390. mov al,VGAREG_ENABLESETRESET ; enable set/reset register
  391. mov ah,15 ; mask = write planes from set/reset
  392. out dx,ax ; load enable set/reset register
  393. mov ax,VGA_BUFFER_SEG
  394. mov es,ax
  395. mov cx,vcr_h ; get row count
  396. nextrow:
  397. push cx
  398. mov di,vcr_FirstByte ; es:di -> first byte in target
  399. add word ptr vcr_FirstByte,VGA_BYTES_PER_SCAN_LINE
  400. ;
  401. ; Do partial byte at relevent part of start of line
  402. ;
  403. mov al,VGAREG_BITMASK
  404. mov ah,vcr_startmask
  405. out dx,ax
  406. mov al,es:[di] ; load latches
  407. stosb ; update video memory
  408. ;
  409. ; Do all full bytes on this line
  410. ;
  411. mov al,VGAREG_BITMASK
  412. mov ah,255
  413. out dx,ax
  414. mov cl,vcr_fullbytes
  415. mov ch,0
  416. rep stosb
  417. ;
  418. ; And finally the trail part
  419. ;
  420. mov al,VGAREG_BITMASK
  421. mov ah,vcr_endmask
  422. out dx,ax
  423. mov al,es:[di] ; load latches
  424. stosb ; update video memory
  425. pop cx ; restore row count
  426. loop nextrow
  427. ;
  428. ; Restore default enable set/reset, bitmask
  429. ;
  430. mov al,VGAREG_ENABLESETRESET
  431. mov ah,0
  432. mov dx,VGA_REGISTER
  433. out dx,ax
  434. mov al,VGAREG_BITMASK
  435. mov ah,255
  436. out dx,ax
  437. pop di
  438. leave
  439. ret
  440. _VgaClearRegion endp
  441. ;++
  442. ;
  443. ; VOID
  444. ; _far
  445. ; VgaClearScreen(
  446. ; IN BYTE PixelValue
  447. ; );
  448. ;
  449. ; Routine Description:
  450. ;
  451. ; Clears the 640x480 vga graphics screen.
  452. ;
  453. ; Arguments:
  454. ;
  455. ; PixelValue - supplies pixel value to clear to.
  456. ;
  457. ; Return Value:
  458. ;
  459. ; None.
  460. ;
  461. ;--
  462. vcs_PixelValue equ [bp+6]
  463. public _VgaClearScreen
  464. _VgaClearScreen proc far
  465. push bp
  466. mov bp,sp
  467. push vcs_PixelValue
  468. push VGA_HEIGHT_SCAN_LINES
  469. push VGA_WIDTH_PIXELS
  470. push 0
  471. push 0
  472. call _VgaClearRegion
  473. add sp,10
  474. leave
  475. ret
  476. _VgaClearScreen endp
  477. ;++
  478. ;
  479. ; VOID
  480. ; _far
  481. ; VgaInit(
  482. ; VOID
  483. ; );
  484. ;
  485. ; Routine Description:
  486. ;
  487. ; Initializes display support by setting 640x480 vga graphics mode
  488. ; and clearing the screen.
  489. ;
  490. ; Arguments:
  491. ;
  492. ; None.
  493. ;
  494. ; Return Value:
  495. ;
  496. ; None.
  497. ;
  498. ;--
  499. public _VgaInit
  500. _VgaInit proc far
  501. mov ax,12h
  502. mov bx,0
  503. int 10h
  504. push 0 ; pixel value
  505. call _VgaClearScreen
  506. add sp,2
  507. ret
  508. _VgaInit endp
  509. ;++
  510. ;
  511. ; FPVOID
  512. ; _far
  513. ; VgaSaveBlock(
  514. ; IN USHORT x,
  515. ; IN USHORT y,
  516. ; IN USHORT w,
  517. ; IN USHORT h,
  518. ; IN FPUSHORT BytesPerRow
  519. ; );
  520. ;
  521. ; Routine Description:
  522. ;
  523. ; Grabs a block of memory from the screen and places it into a block
  524. ; of memory. The block can later be used with VgaBitBlt.
  525. ;
  526. ; This routine limits the size of the block to 32K. If the block
  527. ; would be larger than this, the routine fails.
  528. ;
  529. ; Arguments:
  530. ;
  531. ; x - supplies x coordinate of left edge of area to save.
  532. ; This MUST be an even multiple of 8. If it is not, the routine
  533. ; fails and returns NULL.
  534. ;
  535. ; y - supplies y coordinate of top of area to save.
  536. ;
  537. ; w - supplies width in pixels of area to save. This MUST be an even
  538. ; multiple of 8. If it is not, the routine fails and returns NULL.
  539. ;
  540. ; h - supplies height of area to save.
  541. ;
  542. ; BytesPerRow - receives number of bytes in a row of the saved block.
  543. ; This value is suitable for passing in as the BytesPerRow parameter
  544. ; to VgaBitBlt.
  545. ;
  546. ; Return Value:
  547. ;
  548. ; Pointer to bits, allocated via malloc(). Caller can free when finished.
  549. ; NULL if couldn't allocate enough memory for the saved block or invalid
  550. ; parameters were given.
  551. ;
  552. ;--
  553. vsb_x equ word ptr [bp+6]
  554. vsb_y equ word ptr [bp+8]
  555. vsb_w equ word ptr [bp+10]
  556. vsb_h equ word ptr [bp+12]
  557. vsb_BytesPerRow equ dword ptr [bp+14]
  558. vsb_BytesLeft equ word ptr [bp-2]
  559. MakeAndSave2Pixels label near
  560. ;
  561. ; Take the 4 bytes from the bit planes and based on the top
  562. ; 2 bits of each, build 2 pixel values and save into the
  563. ; target bitmap. We take advantage of the fact that the sar
  564. ; instruction replicates the top bit as it shifts. This allows
  565. ; us to essentially shift 2 adjacent bits into nonadjacent
  566. ; bit positions.
  567. ;
  568. mov dx,bx
  569. mov ax,cx
  570. sar dh,3 ; shift bit 7 to bit 7 and bit 6 to bit 3
  571. sar dl,4 ; shift bit 7 to bit 6 and bit 6 to bit 2
  572. sar ah,5 ; shift bit 7 to bit 5 and bit 6 to bit 1
  573. sar al,6 ; shift bit 7 to bit 4 and bit 6 to bit 0
  574. and dx,8844h ; isolate top 2 bits for each of two pixels
  575. and ax,2211h ; and then isolate bottom 2 bits
  576. or al,ah ; or in bit 1 for each pixel
  577. or al,dl ; or in bit 2 for each pixel
  578. or al,dh ; or in bit 3 for each pixel
  579. stosb ; al has 2 pixel values, save in bitmap
  580. retn
  581. public _VgaSaveBlock
  582. _VgaSaveBlock proc far
  583. push bp
  584. mov bp,sp
  585. sub sp,2
  586. push es
  587. push si
  588. push di
  589. push bx
  590. push ds
  591. ;
  592. ; Check conditions. Violation of them is catastrophic
  593. ; as the implementation simply can't handle it.
  594. ;
  595. test vsb_x,7
  596. jnz vsbinval
  597. test vsb_w,7
  598. jz vsbok
  599. vsbinval:
  600. xor ax,ax
  601. mov dx,ax
  602. jmp vsb_done
  603. ;
  604. ; Calculate the bytes per row. Rows are padded to a dword boundary.
  605. ; There are 4 bits per pixel and 8 bits per byte, so we can simply divide
  606. ; the width by 2 to calculate this value. Note that the result is
  607. ; always naturally aligned to a dword boundary.
  608. ;
  609. vsbok:
  610. mov ax,vsb_w
  611. shr vsb_w,3 ; width is now expressed in bytes
  612. shr ax,1
  613. les di,vsb_BytesPerRow
  614. mov es:[di],ax ; store in caller's variable
  615. ;
  616. ; Calculate the size of the buffer we need, which is simply
  617. ; height * bytes-per-row. Allocate a buffer and zero it out.
  618. ;
  619. ; NOTE: we have not disturbed ds yet. ds MUST be the caller's value
  620. ; before we attempt to call the CRTs!
  621. ;
  622. ; ax = bytes per row
  623. ;
  624. mul vsb_h ; ax = buffer size needed
  625. cmp dx,0 ; > 64K?
  626. jnz vsbinval ; yes, too big
  627. cmp ah,0 ; > 32K?
  628. jl vsbinval ; yes, too big
  629. push ax ; pass size to malloc
  630. call _malloc
  631. pop cx ; throw away size param off stack
  632. mov bx,dx
  633. or bx,ax
  634. jz vsb_done ; dx:ax set for error return
  635. mov es,dx
  636. mov di,ax ; es:di -> bits
  637. push ax ; save offset for later return
  638. mov ax,VGA_BYTES_PER_SCAN_LINE
  639. mul vsb_y ; ax -> first byte on first line
  640. shr vsb_x,3 ; convert x coord to byte count
  641. add vsb_x,ax ; vsb_x -> first byte to save
  642. mov ax,VGA_BUFFER_SEG
  643. mov ds,ax ; ds addresses vga video buffer
  644. vsbrow:
  645. mov si,vsb_x ; ds:si -> first byte in vga buf to save
  646. add vsb_x,VGA_BYTES_PER_SCAN_LINE ; prepare for next iteration
  647. mov dx,vsb_w
  648. mov vsb_BytesLeft,dx
  649. vsbbyte:
  650. ;
  651. ; Read the 4 planes at the current byte
  652. ;
  653. mov dx,VGA_REGISTER
  654. mov al,VGAREG_READMAPSELECT
  655. mov ah,3
  656. out dx,ax
  657. mov bh,[si]
  658. dec ah
  659. out dx,ax
  660. mov bl,[si]
  661. dec ah
  662. out dx,ax
  663. mov ch,[si]
  664. dec ah
  665. out dx,ax
  666. mov cl,[si]
  667. inc si
  668. ;
  669. ; bh = byte from plane 3
  670. ; bl = byte from plane 2
  671. ; ch = byte from plane 1
  672. ; cl = byte from plane 0
  673. ;
  674. call MakeAndSave2Pixels
  675. shl bx,2
  676. shl cx,2
  677. call MakeAndSave2Pixels
  678. shl bx,2
  679. shl cx,2
  680. call MakeAndSave2Pixels
  681. shl bx,2
  682. shl cx,2
  683. call MakeAndSave2Pixels
  684. ;
  685. ; Do all bytes on this line.
  686. ;
  687. dec vsb_BytesLeft
  688. jnz vsbbyte
  689. ;
  690. ; Done with line, see if done.
  691. ;
  692. dec vsb_h
  693. jnz vsbrow
  694. ;
  695. ; Done, return buffer to caller.
  696. ;
  697. mov dx,es
  698. pop ax ; dx:ax -> bit buffer
  699. vsb_done:
  700. pop ds
  701. pop bx
  702. pop di
  703. pop si
  704. pop es
  705. leave
  706. ret
  707. _VgaSaveBlock endp
  708. END