--- /dev/null
+;-----------------------------------------------------------------------\r
+; MODULE XPBITMAP\r
+;\r
+; Planar Bitmap functions - System Ram <-> Video Ram\r
+;\r
+; Compile with Tasm.\r
+; C callable.\r
+;\r
+;\r
+; ****** XLIB - Mode X graphics library ****************\r
+; ****** ****************\r
+; ****** Written By Themie Gouthas ****************\r
+;\r
+; egg@dstos3.dsto.gov.au\r
+; teg@bart.dsto.gov.au\r
+;-----------------------------------------------------------------------\r
+\r
+\r
+COMMENT $\r
+\r
+ This module implements a set of functions to operate on planar bitmaps.\r
+ Planar bitmaps as used by these functions have the following structure:\r
+\r
+ BYTE 0 The bitmap width in bytes (4 pixel groups) range 1..255\r
+ BYTE 1 The bitmap height in rows range 1..255\r
+ BYTE 2..n1 The plane 0 pixels width*height bytes\r
+ BYTE n1..n2 The plane 1 pixels width*height bytes\r
+ BYTE n2..n3 The plane 2 pixels width*height bytes\r
+ BYTE n3..n4 The plane 3 pixels width*height bytes\r
+\r
+ These functions provide the fastest possible bitmap blts from system ram to\r
+ to video and further, the single bitmap is applicable to all pixel\r
+ allignments. The masked functions do not need separate masks since all non\r
+ zero pixels are considered to be masking pixels, hence if a pixel is 0 the\r
+ corresponding screen destination pixel is left unchanged.\r
+\r
+\r
+$\r
+\r
+include xlib.inc\r
+include xpbitmap.inc\r
+LOCALS\r
+ .code\r
+\r
+;----------------------------------------------------------------------\r
+; x_flip_masked_pbm - mask write a planar bitmap from system ram to video ram\r
+; all zero source bitmap bytes indicate destination byte to be left unchanged\r
+; If "Orientation" is set then the bitmap is flipped from left to right as\r
+; it is drawn\r
+;\r
+; Source Bitmap structure:\r
+;\r
+; Width:byte, Height:byte, Bitmap data (plane 0)...Bitmap data (plane 1)..,\r
+; Bitmap data (plane 2)..,Bitmap data (plane 3)..\r
+;\r
+; note width is in bytes ie lots of 4 pixels\r
+;\r
+; x_flip_masked_pbm(X,Y,ScrnOffs,char far * Bitmap, Orientation)\r
+;\r
+;\r
+; LIMITATIONS: No clipping is supported\r
+; Only supports bitmaps with widths which are a multiple of\r
+; 4 pixels\r
+;\r
+; Written by Themie Gouthas\r
+;----------------------------------------------------------------------\r
+_x_flip_masked_pbm proc\r
+ ARG X:word,Y:word,ScrnOffs:word,Bitmap:dword,Orientation:word\r
+ LOCAL Plane:byte,BMHeight:byte,LineInc:word=LocalStk\r
+ push bp\r
+ mov bp,sp\r
+ sub sp,LocalStk ; Create space for local variables\r
+ push si\r
+ push di\r
+ push ds\r
+ cld\r
+ mov ax,SCREEN_SEG\r
+ mov es,ax\r
+ mov ax,[Y] ; Calculate dest screen row\r
+ mov bx,[_ScrnLogicalByteWidth] ; by mult. dest Y coord by Screen\r
+ mul bx ; width then adding screen offset\r
+ mov di,[ScrnOffs] ; store result in DI\r
+ add di,ax\r
+ mov cx,[X] ; Load X coord into CX and make a\r
+ mov dx,cx ; copy in DX\r
+ shr dx,2 ; Find starting byte in dest row\r
+ add di,dx ; add to DI giving screen offset of\r
+ ; first pixel's byte\r
+ lds si,[Bitmap] ; DS:SI -> Bitmap data\r
+ lodsw ; Al = B.M. width (bytes) AH = B.M.\r
+ ; height\r
+ cmp Orientation,0\r
+ jz UnFlippedMasked\r
+\r
+ mov [BMHeight],ah ; Save source bitmap dimensions\r
+ xor ah,ah ; LineInc = bytes to the begin.\r
+ add bx,ax ; of bitmaps next row on screen\r
+ mov [LineInc],bx\r
+ mov bh,al ; Use bh as column loop count\r
+ and cx,0003h ; mask X coord giving plane of 1st\r
+ ; bitmap pixel(zero CH coincidentally)\r
+ mov ah,11h ; Init. mask for VGA plane selection\r
+ shl ah,cl ; Shift for starting pixel plane\r
+ mov dx,SC_INDEX ; Prepare VGA for cpu to video writes\r
+ mov al,MAP_MASK\r
+ out dx,al\r
+ inc dx\r
+ mov [Plane],4 ; Set plane counter to 4\r
+@@PlaneLoop:\r
+ push di ; Save bitmap's start dest. offset\r
+ mov bl,[BMHeight] ; Reset row counter (BL)\r
+ mov al,ah\r
+ out dx,al ; set vga write plane\r
+@@RowLoop:\r
+ mov cl,bh ; Reset Column counter cl\r
+@@ColLoop:\r
+ lodsb ; Get next source bitmap byte\r
+ or al,al ; If not zero then write to dest.\r
+ jz @@NoPixel ; otherwise skip to next byte\r
+ mov es:[di],al\r
+@@NoPixel:\r
+ dec di\r
+ loop @@ColLoop ; loop if more columns left\r
+ add di,[LineInc] ; Move to next row\r
+ dec bl ; decrement row counter\r
+ jnz @@RowLoop ; Jump if more rows left\r
+ pop di ; Restore bitmaps start dest byte\r
+ ror ah,1 ; Shift mask for next plane\r
+ sbb di,0 ; If wrapped increment dest address\r
+ dec [Plane] ; Decrement plane counter\r
+ jnz @@PlaneLoop ; Jump if more planes left\r
+\r
+ pop ds ; restore data segment\r
+ pop di ; restore registers\r
+ pop si\r
+ mov sp,bp ; dealloc local variables\r
+ pop bp\r
+ ret\r
+_x_flip_masked_pbm endp\r
+\r
+\r
+;----------------------------------------------------------------------\r
+; x_put_masked_pbm - mask write a planar bitmap from system ram to video ram\r
+; all zero source bitmap bytes indicate destination byte to be left unchanged\r
+;\r
+; Source Bitmap structure:\r
+;\r
+; Width:byte, Height:byte, Bitmap data (plane 0)...Bitmap data (plane 1)..,\r
+; Bitmap data (plane 2)..,Bitmap data (plane 3)..\r
+;\r
+; note width is in bytes ie lots of 4 pixels\r
+;\r
+; x_put_masked_pbm(X,Y,ScrnOffs,char far * Bitmap)\r
+;\r
+;\r
+; LIMITATIONS: No clipping is supported\r
+; Only supports bitmaps with widths which are a multiple of\r
+; 4 pixels\r
+;\r
+; Written by Themie Gouthas\r
+;----------------------------------------------------------------------\r
+_x_put_masked_pbm proc\r
+ ARG X:word,Y:word,ScrnOffs:word,Bitmap:dword\r
+ LOCAL Plane:byte,BMHeight:byte,LineInc:word=LocalStk\r
+ push bp\r
+ mov bp,sp\r
+ sub sp,LocalStk ; Create space for local variables\r
+ push si\r
+ push di\r
+ push ds\r
+ cld\r
+ mov ax,SCREEN_SEG\r
+ mov es,ax\r
+ mov ax,[Y] ; Calculate dest screen row\r
+ mov bx,[_ScrnLogicalByteWidth] ; by mult. dest Y coord by Screen\r
+ mul bx ; width then adding screen offset\r
+ mov di,[ScrnOffs] ; store result in DI\r
+ add di,ax\r
+ mov cx,[X] ; Load X coord into CX and make a\r
+ mov dx,cx ; copy in DX\r
+ shr dx,2 ; Find starting byte in dest row\r
+ add di,dx ; add to DI giving screen offset of\r
+ ; first pixel's byte\r
+ lds si,[Bitmap] ; DS:SI -> Bitmap data\r
+ lodsw ; Al = B.M. width (bytes) AH = B.M.\r
+ ; height\r
+UnFlippedMasked:\r
+ mov [BMHeight],ah ; Save source bitmap dimensions\r
+ xor ah,ah ; LineInc = bytes to the begin.\r
+ sub bx,ax ; of bitmaps next row on screen\r
+ mov [LineInc],bx\r
+ mov bh,al ; Use bh as column loop count\r
+ and cx,0003h ; mask X coord giving plane of 1st\r
+ ; bitmap pixel(zero CH coincidentally)\r
+ mov ah,11h ; Init. mask for VGA plane selection\r
+ shl ah,cl ; Shift for starting pixel plane\r
+ mov dx,SC_INDEX ; Prepare VGA for cpu to video writes\r
+ mov al,MAP_MASK\r
+ out dx,al\r
+ inc dx\r
+ mov [Plane],4 ; Set plane counter to 4\r
+@@PlaneLoop:\r
+ push di ; Save bitmap's start dest. offset\r
+ mov bl,[BMHeight] ; Reset row counter (BL)\r
+ mov al,ah\r
+ out dx,al ; set vga write plane\r
+@@RowLoop:\r
+ mov cl,bh ; Reset Column counter cl\r
+@@ColLoop:\r
+ lodsb ; Get next source bitmap byte\r
+ or al,al ; If not zero then write to dest.\r
+ jz @@NoPixel ; otherwise skip to next byte\r
+ mov es:[di],al\r
+@@NoPixel:\r
+ inc di\r
+ loop @@ColLoop ; loop if more columns left\r
+ add di,[LineInc] ; Move to next row\r
+ dec bl ; decrement row counter\r
+ jnz @@RowLoop ; Jump if more rows left\r
+ pop di ; Restore bitmaps start dest byte\r
+ rol ah,1 ; Shift mask for next plane\r
+ adc di,0 ; If wrapped increment dest address\r
+ dec [Plane] ; Decrement plane counter\r
+ jnz @@PlaneLoop ; Jump if more planes left\r
+\r
+ pop ds ; restore data segment\r
+ pop di ; restore registers\r
+ pop si\r
+ mov sp,bp ; dealloc local variables\r
+ pop bp\r
+ ret\r
+_x_put_masked_pbm endp\r
+\r
+\r
+\r
+;----------------------------------------------------------------------\r
+; x_put_pbm - Write a planar bitmap from system ram to video ram\r
+;\r
+; Source Bitmap structure:\r
+;\r
+; Width:byte, Height:byte, Bitmap data (plane 0)...Bitmap data (plane 1)..,\r
+; Bitmap data (plane 2)..,Bitmap data (plane 3)..\r
+;\r
+; note width is in bytes ie lots of 4 pixels\r
+;\r
+; x_put_pbm(X,Y,ScrnOffs,char far * Bitmap)\r
+;\r
+;\r
+; LIMITATIONS: No clipping is supported\r
+; Only supports bitmaps with widths which are a multiple of\r
+; 4 pixels\r
+; FEATURES : Automatically selects REP MOVSB or REP MOVSW depending on\r
+; source bitmap width, by modifying opcode ;-).\r
+;\r
+; Written by Themie Gouthas\r
+;----------------------------------------------------------------------\r
+\r
+\r
+_x_put_pbm proc\r
+ ARG X:word,Y:word,ScrnOffs:word,Bitmap:dword\r
+ LOCAL Plane:byte,BMHeight:byte,LineInc:word=LocalStk\r
+ push bp\r
+ mov bp,sp\r
+ sub sp,LocalStk ; Create space for local variables\r
+ push si\r
+ push di\r
+ push ds\r
+ cld\r
+ mov ax,SCREEN_SEG\r
+ mov es,ax\r
+ mov ax,[Y] ; Calculate dest screen row\r
+ mov bx,[_ScrnLogicalByteWidth] ; by mult. dest Y coord by Screen\r
+ mul bx ; width then adding screen offset\r
+ mov di,[ScrnOffs] ; store result in DI\r
+ add di,ax\r
+ mov cx,[X] ; Load X coord into CX and make a\r
+ mov dx,cx ; copy in DX\r
+ shr dx,2 ; Find starting byte in dest row\r
+ add di,dx ; add to DI giving screen offset of\r
+ ; first pixel's byte\r
+ lds si,[Bitmap] ; DS:SI -> Bitmap data\r
+ lodsw ; Al = B.M. width (bytes) AH = B.M.\r
+ ; height\r
+UnFlipped:\r
+ mov [BMHeight],ah ; Save source bitmap dimensions\r
+ xor ah,ah ; LineInc = bytes to the begin.\r
+ sub bx,ax ; of bitmaps next row on screen\r
+ mov [LineInc],bx\r
+ mov bh,al\r
+ ; Self Modifying, Shame, shame shame..\r
+ and cx,0003h ; mask X coord giving plane of 1st\r
+ ; bitmap pixel(zero CH coincidentally)\r
+ mov ah,11h ; Init. mask for VGA plane selection\r
+ shl ah,cl ; Shift for starting pixel plane\r
+ mov dx,SC_INDEX ; Prepare VGA for cpu to video writes\r
+ mov al,MAP_MASK\r
+ out dx,al\r
+ inc dx\r
+ mov [Plane],4 ; Set plane counter to 4\r
+@@PlaneLoop:\r
+ push di\r
+ mov bl,[BMHeight]\r
+ mov al,ah\r
+ out dx,al\r
+@@RowLoop:\r
+ mov cl,bh\r
+ shr cl,1\r
+ rep movsw ; Copy a complete row for curr plane\r
+ adc cl,0\r
+ rep movsb\r
+ add di,[LineInc] ; Move to next row\r
+ dec bl ; decrement row counter\r
+ jnz @@RowLoop ; Jump if more rows left\r
+ pop di ; Restore bitmaps start dest byte\r
+ rol ah,1 ; Shift mask for next plane\r
+ adc di,0 ; If wrapped increment dest address\r
+ dec [Plane] ; Decrement plane counter\r
+ jnz @@PlaneLoop ; Jump if more planes left\r
+\r
+ pop ds ; restore data segment\r
+ pop di ; restore registers\r
+ pop si\r
+ mov sp,bp ; dealloc local variables\r
+ pop bp\r
+ ret\r
+_x_put_pbm endp\r
+\r
+;----------------------------------------------------------------------\r
+; x_flip_pbm - Write a planar bitmap from system ram to video ram\r
+; If "Orientation" is set then the bitmap is flipped from left to right as\r
+; it is drawn\r
+\r
+;\r
+; Source Bitmap structure:\r
+;\r
+; Width:byte, Height:byte, Bitmap data (plane 0)...Bitmap data (plane 1)..,\r
+; Bitmap data (plane 2)..,Bitmap data (plane 3)..\r
+;\r
+; note width is in bytes ie lots of 4 pixels\r
+;\r
+; x_flip_pbm(X,Y,ScrnOffs,char far * Bitmap, WORD orientation)\r
+;\r
+;\r
+; LIMITATIONS: No clipping is supported\r
+; Only supports bitmaps with widths which are a multiple of\r
+; 4 pixels\r
+;\r
+; NOTES: The flipped put function is slower than the standard put function\r
+;\r
+; Written by Themie Gouthas\r
+;----------------------------------------------------------------------\r
+_x_flip_pbm proc\r
+ ARG X:word,Y:word,ScrnOffs:word,Bitmap:dword,Orientation:word\r
+ LOCAL Plane:byte,BMHeight:byte,LineInc:word=LocalStk\r
+ push bp\r
+ mov bp,sp\r
+ sub sp,LocalStk ; Create space for local variables\r
+ push si\r
+ push di\r
+ push ds\r
+ cld\r
+ mov ax,SCREEN_SEG\r
+ mov es,ax\r
+ mov ax,[Y] ; Calculate dest screen row\r
+ mov bx,[_ScrnLogicalByteWidth] ; by mult. dest Y coord by Screen\r
+ mul bx ; width then adding screen offset\r
+ mov di,[ScrnOffs] ; store result in DI\r
+ add di,ax\r
+ mov cx,[X] ; Load X coord into CX and make a\r
+ mov dx,cx ; copy in DX\r
+ shr dx,2 ; Find starting byte in dest row\r
+ add di,dx ; add to DI giving screen offset of\r
+ ; first pixel's byte\r
+ lds si,[Bitmap] ; DS:SI -> Bitmap data\r
+ lodsw ; Al = B.M. width (bytes) AH = B.M.\r
+ ; height\r
+ cmp Orientation,0\r
+ jz UnFlipped\r
+\r
+ mov [BMHeight],ah ; Save source bitmap dimensions\r
+ xor ah,ah ; LineInc = bytes to the begin.\r
+ add bx,ax ; of bitmaps next row on screen\r
+ mov [LineInc],bx\r
+ mov bh,al ; Use bh as column loop count\r
+ and cx,0003h ; mask X coord giving plane of 1st\r
+ ; bitmap pixel(zero CH coincidentally)\r
+ mov ah,11h ; Init. mask for VGA plane selection\r
+ shl ah,cl ; Shift for starting pixel plane\r
+ mov dx,SC_INDEX ; Prepare VGA for cpu to video writes\r
+ mov al,MAP_MASK\r
+ out dx,al\r
+ inc dx\r
+ mov [Plane],4 ; Set plane counter to 4\r
+@@PlaneLoop:\r
+ push di ; Save bitmap's start dest. offset\r
+ mov bl,[BMHeight] ; Reset row counter (BL)\r
+ mov al,ah\r
+ out dx,al ; set vga write plane\r
+@@RowLoop:\r
+ mov cl,bh ; Reset Column counter cl\r
+@@ColLoop:\r
+ lodsb\r
+ mov es:[di],al\r
+ dec di\r
+ sub di,2\r
+ loop @@ColLoop ; loop if more columns left\r
+@@DoneCol:\r
+ add di,[LineInc] ; Move to next row\r
+ dec bl ; decrement row counter\r
+ jnz @@RowLoop ; Jump if more rows left\r
+ pop di ; Restore bitmaps start dest byte\r
+ ror ah,1 ; Shift mask for next plane\r
+ sbb di,0 ; If wrapped increment dest address\r
+ dec [Plane] ; Decrement plane counter\r
+ jnz @@PlaneLoop ; Jump if more planes left\r
+\r
+ pop ds ; restore data segment\r
+ pop di ; restore registers\r
+ pop si\r
+ mov sp,bp ; dealloc local variables\r
+ pop bp\r
+ ret\r
+_x_flip_pbm endp\r
+\r
+\r
+;----------------------------------------------------------------------\r
+; x_get_pbm - Read a planar bitmap to system ram from video ram\r
+;\r
+; Source Bitmap structure:\r
+;\r
+; Width:byte, Height:byte, Bitmap data (plane 0)...Bitmap data (plane 1)..,\r
+; Bitmap data (plane 2)..,Bitmap data (plane 3)..\r
+;\r
+; note width is in bytes ie lots of 4 pixels\r
+;\r
+; x_get_pbm(X,Y,BMwidth,BMheight,ScrnOffs,char far * Bitmap)\r
+;\r
+;\r
+; LIMITATIONS: No clipping is supported\r
+; Only supports bitmaps with widths which are a multiple of\r
+; 4 pixels\r
+; FEATURES : Automatically selects REP MOVSB or REP MOVSW depending on\r
+; source bitmap width, by modifying opcode ;-).\r
+;\r
+; Written by Themie Gouthas\r
+;----------------------------------------------------------------------\r
+_x_get_pbm proc\r
+ ARG X:word,Y:word,SrcWidth:byte,SrcHeight:byte,ScrnOffs:word,Bitmap:dword\r
+ LOCAL Plane:byte,LineInc:word=LocalStk\r
+ push bp\r
+ mov bp,sp\r
+ sub sp,LocalStk ; Create space for local variables\r
+ push si\r
+ push di\r
+ push ds\r
+ cld\r
+\r
+ mov ax,[Y] ; Calculate screen row\r
+ mov bx,[_ScrnLogicalByteWidth] ; by mult. Y coord by Screen\r
+ mul bx ; width then adding screen offset\r
+ mov si,[ScrnOffs] ; store result in SI\r
+ add si,ax\r
+ mov cx,[X] ; Load X coord into CX and make a\r
+ mov dx,cx ; copy in DX\r
+ shr dx,2 ; Find starting byte in screen row\r
+ add si,dx ; add to SI giving screen offset of\r
+ ; first pixel's byte\r
+ mov ax,SCREEN_SEG\r
+ mov ds,ax\r
+ les di,[Bitmap] ; ES:DI -> Bitmap data\r
+ mov al,[SrcWidth]\r
+ mov ah,[SrcHeight]\r
+ stosw ; Al = B.M. width (bytes) AH = B.M.\r
+ ; height\r
+ xor ah,ah ; LineInc = bytes to the begin.\r
+ sub bx,ax ; of bitmaps next row on screen\r
+ mov [LineInc],bx\r
+ mov bh,al\r
+ ; Self Modifying, Shame, shame shame..\r
+ and cx,0003h ; mask X coord giving plane of 1st\r
+ ; bitmap pixel(zero CH coincidentally)\r
+ mov ah,11h ; Init. mask for VGA plane selection\r
+ shl ah,cl ; Shift for starting pixel plane\r
+ mov dx,GC_INDEX ; Prepare VGA for cpu to video reads\r
+ mov al,READ_MAP\r
+ out dx,al\r
+ inc dx\r
+ mov [Plane],4 ; Set plane counter (BH) to 4\r
+ mov al,cl\r
+@@PlaneLoop:\r
+ push si\r
+ mov bl,[SrcHeight]\r
+ out dx,al\r
+@@RowLoop:\r
+ mov cl,bh\r
+ shr cl,1\r
+ rep movsw ; Copy a complete row for curr plane\r
+ adc cl,0\r
+ rep movsb\r
+ add si,[LineInc] ; Move to next row\r
+ dec bl ; decrement row counter\r
+ jnz @@RowLoop ; Jump if more rows left\r
+ pop si ; Restore bitmaps start dest byte\r
+\r
+ inc al ; Select next plane to read from\r
+ and al,3 ;\r
+\r
+ rol ah,1 ; Shift mask for next plane\r
+ adc si,0 ; If wrapped increment dest address\r
+ dec [Plane] ; Decrement plane counter\r
+ jnz @@PlaneLoop ; Jump if more planes left\r
+\r
+ pop ds ; restore data segment\r
+ pop di ; restore registers\r
+ pop si\r
+ mov sp,bp ; dealloc local variables\r
+ pop bp\r
+ ret\r
+_x_get_pbm endp\r
+\r
+\r
+\r
+\r
+ end\r
+\r
+\r
+\r
+ ARG X:word,Y:word,ScrnOffs:word,Bitmap:dword,Orientation:word\r
+ LOCAL Plane:byte,BMHeight:byte,LineInc:word,Columns:byte=LocalStk\r
+ push bp\r
+ mov bp,sp\r
+ sub sp,LocalStk ; Create space for local variables\r
+ push si\r
+ push di\r
+ push ds\r
+ cld\r
+ mov ax,SCREEN_SEG\r
+ mov es,ax\r
+ mov ax,[Y] ; Calculate dest screen row\r
+ mov bx,[_ScrnLogicalByteWidth] ; by mult. dest Y coord by Screen\r
+ mul bx ; width then adding screen offset\r
+ mov di,[ScrnOffs] ; store result in DI\r
+ add di,ax\r
+ mov cx,[X] ; Load X coord into CX and make a\r
+ mov dx,cx ; copy in DX\r
+ shr dx,2 ; Find starting byte in dest row\r
+ add di,dx ; add to DI giving screen offset of\r
+ ; first pixel's byte\r
+ lds si,[Bitmap] ; DS:SI -> Bitmap data\r
+ lodsw ; Al = B.M. width (bytes) AH = B.M.\r
+ ; height\r
+ cmp Orientation,0\r
+ jz UnFlipped\r
+\r
+ mov [BMHeight],ah ; Save source bitmap dimensions\r
+ xor ah,ah ; LineInc = bytes to the begin.\r
+ add bx,ax ; of bitmaps next row on screen\r
+ mov [LineInc],bx\r
+ mov [Columns],al ; Use bh as column loop count\r
+ and cx,0003h ; mask X coord giving plane of 1st\r
+ ; bitmap pixel(zero CH coincidentally)\r
+ mov ah,11h ; Init. mask for VGA plane selection\r
+ shl ah,cl ; Shift for starting pixel plane\r
+ mov bh,ah\r
+ mov dx,SC_INDEX ; Prepare VGA for cpu to video writes\r
+ mov al,MAP_MASK\r
+ out dx,al\r
+ inc dx\r
+ mov [Plane],4 ; Set plane counter to 4\r
+@@PlaneLoop:\r
+ push di ; Save bitmap's start dest. offset\r
+ mov bl,[BMHeight] ; Reset row counter (BL)\r
+ mov al,bh\r
+ out dx,al ; set vga write plane\r
+@@RowLoop:\r
+ mov cl,[Columns] ; Reset Column counter cl\r
+ shr cx,1\r
+ jnc @@ColLoop\r
+ lodsb\r
+ mov es:[di],al\r
+ dec di\r
+@@ColLoop:\r
+ lodsw ; Get next source bitmap byte\r
+ xchg al,ah\r
+ mov es:[di],ax\r
+ sub di,2\r
+ loop @@ColLoop ; loop if more columns left\r
+\r
+ add di,[LineInc] ; Move to next row\r
+ dec bl ; decrement row counter\r
+ jnz @@RowLoop ; Jump if more rows left\r
+ pop di ; Restore bitmaps start dest byte\r
+ ror bh,1 ; Shift mask for next plane\r
+ sbb di,0 ; If wrapped increment dest address\r
+ dec [Plane] ; Decrement plane counter\r
+ jnz @@PlaneLoop ; Jump if more planes left\r
+\r
+ pop ds ; restore data segment\r
+ pop di ; restore registers\r
+ pop si\r
+ mov sp,bp ; dealloc local variables\r
+ pop bp\r
+ ret
\ No newline at end of file