]> 4ch.mooo.com Git - 16.git/blobdiff - 16/xlib/xpbitmap.asm
refresh wwww
[16.git] / 16 / xlib / xpbitmap.asm
diff --git a/16/xlib/xpbitmap.asm b/16/xlib/xpbitmap.asm
new file mode 100755 (executable)
index 0000000..e0a2d96
--- /dev/null
@@ -0,0 +1,603 @@
+;-----------------------------------------------------------------------\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