; ****************************************************************************
; VGL386.ASM
;
; Simple VGA Mode 13h graphics routines.  Hopefully they are simple *AND*
; fast.  No promises, no guarantees.  They work for me (ATI Wonder on a couple
; of 486's), but they may not be what you're looking for.  These routines
; assume a 386 processor (hence the movsd instructions).  For 286's use the
; VGL286.ASM module instead.
;
; WARNING 1: These routines do absolutely *NO CLIPPING*!  If you happen to draw
; a large sprite right at the edge of your virtual screen and it clobbers
; something, you're on your own.
;
; WARNING 2: To help speed things up I do things like not pushing all the
; registers at the beginning of a routine.  If your C routines use register
; variables (which normally use the SI and DI registers), things may not
; work nicely.  To fix this either don't use register variables, or edit
; this file and add push and pop instructions to save the SI and DI regs.
;
; Mark
; morley@camosun.bc.ca
; ****************************************************************************

LOCALS
.model small
.386

; ****************************************************************************
; EQUates
; ****************************************************************************

VIDEO       equ         0a000h                  ; Start of video memory

; ****************************************************************************
; Data Section
; ****************************************************************************

.data

; Where we're going to draw into (defaults to video memory)
_Buffer                 dw  VIDEO

; Table for quick Y calculations
RowOff                  dw      0,  320,  640,  960, 1280, 1600, 1920, 2240
                        dw   2560, 2880, 3200, 3520, 3840, 4160, 4480, 4800
                        dw   5120, 5440, 5760, 6080, 6400, 6720, 7040, 7360
                        dw   7680, 8000, 8320, 8640, 8960, 9280, 9600, 9920
                        dw  10240,10560,10880,11200,11520,11840,12160,12480
                        dw  12800,13120,13440,13760,14080,14400,14720,15040
                        dw  15360,15680,16000,16320,16640,16960,17280,17600
                        dw  17920,18240,18560,18880,19200,19520,19840,20160
                        dw  20480,20800,21120,21440,21760,22080,22400,22720
                        dw  23040,23360,23680,24000,24320,24640,24960,25280
                        dw  25600,25920,26240,26560,26880,27200,27520,27840
                        dw  28160,28480,28800,29120,29440,29760,30080,30400
                        dw  30720,31040,31360,31680,32000,32320,32640,32960
                        dw  33280,33600,33920,34240,34560,34880,35200,35520
                        dw  35840,36160,36480,36800,37120,37440,37760,38080
                        dw  38400,38720,39040,39360,39680,40000,40320,40640
                        dw  40960,41280,41600,41920,42240,42560,42880,43200
                        dw  43520,43840,44160,44480,44800,45120,45440,45760
                        dw  46080,46400,46720,47040,47360,47680,48000,48320
                        dw  48640,48960,49280,49600,49920,50240,50560,50880
                        dw  51200,51520,51840,52160,52480,52800,53120,53440
                        dw  53760,54080,54400,54720,55040,55360,55680,56000
                        dw  56320,56640,56960,57280,57600,57920,58240,58560
                        dw  58880,59200,59520,59840,60160,60480,60800,61120
                        dw  61440,61760,62080,62400,62720,63040,63360,63680

; ****************************************************************************
; Macros
; ****************************************************************************

                 VSYNC macro

                 mov  dx,03DAh
@@1:
                 in   al,dx
                 test al,8
                 jnz  @@1
@@2:
                 in   al,dx
                 test al,8
                 jz   @@2

                 endm

; ****************************************************************************
; Code Section
; ****************************************************************************

.code

; ============================================================================
; void vglInit( void )
;
; Enter mode 13h.
; ============================================================================

                 public _vglInit

_vglInit         proc near

                 mov  ax,13h
                 int  10h
                 mov  _Buffer,VIDEO

                 ret
_vglInit         endp

; ============================================================================
; void vglTerm( void )
;
; Exit mode 13h.
; ============================================================================

                 public _vglTerm

_vglTerm         proc near

                 mov  ax,3
                 int  10h

                 ret
_vglTerm         endp

; ============================================================================
; void vglBuffer( char far* buffer )
;
; Point the routines at a different area of memory ("virtual" screens)
; ============================================================================

                 public _vglBuffer

_vglBuffer       proc near
                 push bp
                 mov  bp,sp

                 les  ax,[bp+4]
                 mov  _Buffer,es

                 pop  bp
                 ret
_vglBuffer       endp

; ============================================================================
; void vglClear( int c )
;
; Clear the current "screen" to a given color.
; ============================================================================

                 public _vglClear

_vglClear        proc near
                 push bp
                 mov  bp,sp

                 mov  es,_Buffer
                 xor  di,di
                 mov  dl,[bp+4]
                 mov  al,dl
                 mov  ah,dl
                 shl  eax,16
                 mov  al,dl
                 mov  ah,dl
                 mov  cx,16000
                 rep  stosd

                 pop  bp
                 ret
_vglClear        endp

; ============================================================================
; void vglClearL( int lines, int c )
;
; Clear the top 'lines' rows of the current screen to the specified color.
; ============================================================================

                 public _vglClearL

_vglClearL       proc near
                 push bp
                 mov  bp,sp

                 mov  es,_Buffer
                 xor  di,di
                 mov  ax,80
                 mov  bx,[bp+4]
                 mul  bx
                 mov  cx,ax
                 mov  dl,[bp+6]
                 mov  al,dl
                 mov  ah,dl
                 shl  eax,16
                 mov  al,dl
                 mov  ah,dl
                 rep  stosd

                 pop  bp
                 ret
_vglClearL       endp

; ============================================================================
; void vglUpdate( void )
;
; Copy the current "screen" into video memory.  Useless if you're not currently
; drawing into an off-screen buffer.
; ============================================================================

                 public _vglUpdate

_vglUpdate       proc near
                 push bp
                 mov  bp,sp
                 push ds

                 mov  ds,_Buffer
                 xor  si,si
                 mov  ax,VIDEO
                 mov  es,ax
                 xor  di,di
                 mov  cx,16000
                 rep  movsd

                 pop  ds
                 pop  bp
                 ret
_vglUpdate       endp

; ============================================================================
; void vglUpdateW( void )
;
; Same as above but it waits for vertical retrace.
; ============================================================================

                 public _vglUpdateW

_vglUpdateW      proc near
                 push bp
                 mov  bp,sp
                 push ds

                 VSYNC

                 mov  ds,_Buffer
                 xor  si,si
                 mov  ax,VIDEO
                 mov  es,ax
                 xor  di,di
                 mov  cx,16000
                 rep  movsd

                 pop  ds
                 pop  bp
                 ret
_vglUpdateW      endp

; ============================================================================
; void vglUpdateL( int lines )
;
; Same as vglUpdate() except it only copies over the top 'lines' rows.
; ============================================================================

                 public _vglUpdateL

_vglUpdateL      proc near
                 push bp
                 mov  bp,sp
                 push ds

                 mov  ds,_Buffer
                 xor  si,si
                 mov  ax,VIDEO
                 mov  es,ax
                 xor  di,di
                 mov  ax,80
                 mov  bx,[bp+4]
                 mul  bx
                 mov  cx,ax
                 rep  movsd

                 pop  ds
                 pop  bp
                 ret
_vglUpdateL      endp

; ============================================================================
; void vglUpdateLW( int lines )
;
; Same as above but it waits for vertical retrace.
; ============================================================================

                 public _vglUpdateLW

_vglUpdateLW     proc near
                 push bp
                 mov  bp,sp
                 push ds

                 VSYNC

                 mov  ds,_Buffer
                 xor  si,si
                 mov  ax,VIDEO
                 mov  es,ax
                 xor  di,di
                 mov  ax,80
                 mov  bx,[bp+4]
                 mul  bx
                 mov  cx,ax
                 rep  movsd

                 pop  ds
                 pop  bp
                 ret
_vglUpdateLW     endp

; ============================================================================
; void vglBox( int left, int top, int width, int height, int c )
;
; Draw a filled in box at offset x,y that is width x height big, in the
; specified color.  It is draw into the current "screen" (buffer).
; ============================================================================

                 public _vglBox

_vglBox          proc near
                 push bp
                 mov  bp,sp

                 mov  es,_Buffer
                 mov  bx,[bp+6]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+4]
                 mov  cl,[bp+12]
                 mov  al,cl
                 mov  ah,cl
                 shl  eax,16
                 mov  al,cl
                 mov  ah,cl
                 mov  dx,[bp+10]
                 mov  bp,[bp+8]
                 mov  bx,320
                 sub  bx,bp
                 mov  si,bp
                 and  si,3
                 shr  bp,2
BLoop:
                 mov  cx,bp
                 rep  stosd
                 mov  cx,si
                 rep  stosb
                 add  di,bx
                 dec  dx
                 jnz  BLoop

                 pop  bp
                 ret
_vglBox          endp

; ============================================================================
; void vglHLine( int line, int left, int width, int c )
;
; Draw a horizontal line in a specified color.
; ============================================================================

                 public _vglHLine

_vglHLine        proc near
                 push bp
                 mov  bp,sp

                 mov  es,_Buffer
                 mov  bx,[bp+4]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+6]
                 mov  cl,[bp+10]
                 mov  al,cl
                 mov  ah,cl
                 shl  eax,16
                 mov  al,cl
                 mov  ah,cl
                 mov  cx,[bp+8]
                 mov  si,cx
                 and  si,3
                 shr  cx,2
                 rep  stosd
                 mov  cx,si
                 rep  stosb

                 pop  bp
                 ret
_vglHLine        endp

; ============================================================================
; void vglCopy( int left, int top, int width, int height )
;
; Like vglUpdate() except it updates only a region of the screen.
; ============================================================================

                 public _vglCopy

_vglCopy         proc near
                 push bp
                 mov  bp,sp
                 push ds

                 mov  bx,[bp+6]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+4]
                 mov  ds,_Buffer
                 mov  ax,VIDEO
                 mov  es,ax
                 mov  dx,[bp+10]
                 mov  bx,[bp+8]
                 mov  bp,320
                 sub  bp,bx
                 mov  ax,bx
                 and  ax,3
                 shr  bx,2
CLoop:
                 mov  si,di
                 mov  cx,bx
                 rep  movsd
                 mov  cx,ax
                 rep  movsb
                 add  di,bp
                 dec  dx
                 jnz  CLoop

                 pop  ds
                 pop  bp
                 ret
_vglCopy         endp

; ============================================================================
; void vglCopyW( int left, int top, int width, int height )
;
; Same as above but waits for vertical retrace.
; ============================================================================

                 public _vglCopyW

_vglCopyW        proc near
                 push bp
                 mov  bp,sp
                 push ds

                 VSYNC

                 mov  bx,[bp+6]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+4]
                 mov  ds,_Buffer
                 mov  ax,VIDEO
                 mov  es,ax
                 mov  dx,[bp+10]
                 mov  bx,[bp+8]
                 mov  bp,320
                 sub  bp,bx
                 mov  ax,bx
                 and  ax,3
                 shr  bx,2
CWLoop:
                 mov  si,di
                 mov  cx,bx
                 rep  movsd
                 mov  cx,ax
                 rep  movsb
                 add  di,bp
                 dec  dx
                 jnz  CWLoop
CWEnd:
                 pop  ds
                 pop  bp
                 ret
_vglCopyW        endp

; ============================================================================
; void vglPutPel( int x, int y, int c )
;
; Draw a single pixel in the specified color at offset x,y.  Pixel is drawn
; into the current "screen".
; ============================================================================

                 public _vglPutPel

_vglPutPel       proc near
                 push bp
                 mov  bp,sp

                 mov  es,_Buffer
                 mov  bx,[bp+6]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+4]
                 mov  al,[bp+8]
                 mov  [es:di],al

                 pop  bp
                 ret
_vglPutPel       endp

; ============================================================================
; int vglGetPel( int x, int y )
;
; Get the color of the pixel at location x,y in the current "screen".
; ============================================================================

                 public _vglGetPel

_vglGetPel       proc near
                 push bp
                 mov  bp,sp

                 mov  es,_Buffer
                 mov  bx,[bp+6]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+4]
                 mov  al,[bp+8]
                 mov  al,[es:di]

                 pop  bp
                 ret
_vglGetPel       endp

; ============================================================================
; void vglPut( int x, int y, int w, int h, char far* buf )
;
; Draw a bitmap image into the buffer at location x,y.  Every pixel of the
; image is drawn, so no transparent areas are possible.  Very quick.  Good
; for drawing tiles in a tile based game!
; ============================================================================

                 public _vglPut

_vglPut          proc near
                 push bp
                 mov  bp,sp
                 push ds

                 mov  es,_Buffer
                 mov  bx,[bp+6]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+4]
                 lds  si,[bp+12]
                 mov  bx,[bp+8]
                 mov  dx,[bp+10]
                 mov  bp,320
                 sub  bp,bx
                 mov  ax,bx
                 and  ax,3
                 shr  bx,2
PLoop:
                 mov  cx,bx
                 rep  movsd
                 mov  cx,ax
                 rep  movsb
                 add  di,bp
                 dec  dx
                 jnz  PLoop

                 pop  ds
                 pop  bp
                 ret
_vglPut          endp

; ============================================================================
; void vglPutSprite( int x, int y, int w, int h, char far* buf )
;
; Like vglPut() except that any pixels of color 0 will be transparent.  Not
; nearly as fast as vglPut(), but you'll need it for certain sprites, etc.
; ============================================================================

                 public _vglPutSprite

_vglPutSprite    proc near
                 push bp
                 mov  bp,sp
                 push ds

                 mov  es,_Buffer
                 mov  bx,[bp+6]
                 shl  bx,1
                 mov  di,[RowOff+bx]
                 add  di,[bp+4]
                 lds  si,[bp+12]
                 mov  bx,[bp+8]
                 mov  dx,[bp+10]
                 mov  bp,320
                 sub  bp,bx
PSLoop1:
                 mov  cx,bx
PSLoop2:
                 lodsb
                 or   al,al
                 je   PSSkip
                 mov  [es:di],al
PSSkip:
                 inc  di
                 dec  cx
                 jne  PSLoop2
                 add  di,bp
                 dec  dx
                 jnz  PSLoop1

                 pop  ds
                 pop  bp
                 ret
_vglPutSprite    endp

; ============================================================================
; void vglSetPal( char pal[768] )
;
; Set the palette from an array of 256 RGB triples.
; ============================================================================

                 public _vglSetPal

_vglSetPal       proc near
                 push bp
                 mov  bp,sp

                 VSYNC

                 mov  ax,1012h
                 xor  bx,bx
                 mov  cx,256
                 les  dx,[bp+4]
                 int  10h

                 pop  bp
                 ret
_vglSetPal       endp

; ============================================================================
; void vglSetPartPal( int first, int num, char far* pal )
;
; Set a partial palette from an array of RGB triples.
; ============================================================================

                 public _vglSetPartPal

_vglSetPartPal   proc near
                 push bp
                 mov  bp,sp

                 VSYNC

                 mov  ax,1012h
                 mov  bx,[bp+4]
                 mov  cx,[bp+6]
                 les  dx,[bp+8]
                 int  10h

                 pop  bp
                 ret
_vglSetPartPal   endp

end

